summaryrefslogtreecommitdiffstats
path: root/src/H5C.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/H5C.c')
-rw-r--r--src/H5C.c3205
1 files changed, 2308 insertions, 897 deletions
diff --git a/src/H5C.c b/src/H5C.c
index 5cdd65f..fc4e8a5 100644
--- a/src/H5C.c
+++ b/src/H5C.c
@@ -85,12 +85,23 @@
#include "H5FDprivate.h" /* File drivers */
#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 */
#include "H5SLprivate.h" /* Skip lists */
/*
+ * Private 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 */
+
+/*
* Private file-scope variables.
*/
@@ -103,27 +114,21 @@ H5FL_DEFINE_STATIC(H5C_t);
*/
static herr_t H5C__auto_adjust_cache_size(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- hbool_t write_permitted,
- hbool_t * first_flush_ptr);
+ hid_t dxpl_id,
+ hbool_t write_permitted);
static herr_t H5C__autoadjust__ageout(H5F_t * f,
+ hid_t dxpl_id,
double hit_rate,
enum H5C_resize_status * status_ptr,
size_t * new_max_cache_size_ptr,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- hbool_t write_permitted,
- hbool_t * first_flush_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,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- hbool_t write_permitted,
- hbool_t * first_flush_ptr);
+ hid_t dxpl_id,
+ hbool_t write_permitted);
static herr_t H5C__autoadjust__ageout__insert_new_marker(H5C_t * cache_ptr);
@@ -135,19 +140,16 @@ 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_single_entry(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- const H5C_class_t * type_ptr,
- haddr_t addr,
- unsigned flags,
- hbool_t * first_flush_ptr,
- hbool_t del_entry_from_slist_on_destroy);
+static herr_t H5C_flush_single_entry(const H5F_t * f,
+ hid_t dxpl_id,
+ haddr_t addr,
+ unsigned flags,
+ hbool_t del_entry_from_slist_on_destroy,
+ int64_t *entry_size_change_ptr);
-static herr_t H5C_flush_invalidate_cache(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- unsigned flags);
+static herr_t H5C_flush_invalidate_cache(const H5F_t * f,
+ hid_t dxpl_id,
+ unsigned flags);
static void * H5C_load_entry(H5F_t * f,
hid_t dxpl_id,
@@ -156,19 +158,16 @@ static void * H5C_load_entry(H5F_t * f,
void * udata);
static herr_t H5C_make_space_in_cache(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- size_t space_needed,
- hbool_t write_permitted,
- hbool_t * first_flush_ptr);
+ hid_t dxpl_id,
+ size_t space_needed,
+ hbool_t write_permitted);
static herr_t H5C_tag_entry(H5C_t * cache_ptr,
H5C_cache_entry_t * entry_ptr,
hid_t dxpl_id);
static herr_t H5C_flush_tagged_entries(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
+ hid_t dxpl_id,
H5C_t * cache_ptr,
haddr_t tag);
@@ -176,20 +175,29 @@ static herr_t H5C_mark_tagged_entries(H5C_t * cache_ptr,
haddr_t tag);
static herr_t H5C_flush_marked_entries(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
+ hid_t dxpl_id,
H5C_t * cache_ptr);
#if H5C_DO_TAGGING_SANITY_CHECKS
static herr_t H5C_verify_tag(int id, haddr_t tag);
#endif
+#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 */
+#if 0 /* debugging routines */
+herr_t H5C_dump_cache(H5C_t * cache_ptr, const char * cache_name);
+herr_t H5C_dump_cache_skip_list(H5C_t * cache_ptr, char * calling_fcn);
+#endif /* debugging routines */
+
/****************************************************************************
*
@@ -208,25 +216,53 @@ static herr_t H5C_validate_protected_entry_list(H5C_t * cache_ptr);
#define H5C__EPOCH_MARKER_TYPE H5C__MAX_NUM_TYPE_IDS
-static void *H5C_epoch_marker_load(H5F_t *f, hid_t dxpl_id, haddr_t addr,
- void *udata);
-static herr_t H5C_epoch_marker_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest,
- haddr_t addr, void *thing,
- unsigned *flags_ptr);
-static herr_t H5C_epoch_marker_dest(H5F_t *f, void *thing);
-static herr_t H5C_epoch_marker_clear(H5F_t *f, void *thing, hbool_t dest);
-static herr_t H5C_epoch_marker_notify(H5C_notify_action_t action, void *thing);
-static herr_t H5C_epoch_marker_size(const H5F_t *f, const void *thing, size_t *size_ptr);
+static herr_t H5C__epoch_marker_get_load_size(const void *udata_ptr,
+ size_t *image_len_ptr);
+static void * H5C__epoch_marker_deserialize(const void * image_ptr,
+ size_t len,
+ void * udata,
+ hbool_t * dirty_ptr);
+static herr_t H5C__epoch_marker_image_len(const void * thing,
+ size_t *image_len_ptr,
+ hbool_t *compressed_ptr,
+ size_t *compressed_len_ptr);
+static herr_t H5C__epoch_marker_pre_serialize(const H5F_t *f,
+ hid_t dxpl_id,
+ void * thing,
+ haddr_t addr,
+ size_t len,
+ size_t compressed_len,
+ haddr_t * new_addr_ptr,
+ size_t * new_len_ptr,
+ size_t * new_compressed_len_ptr,
+ unsigned * flags_ptr);
+static herr_t H5C__epoch_marker_serialize(const H5F_t *f,
+ void * image_ptr,
+ size_t len,
+ void * thing);
+static herr_t H5C__epoch_marker_notify(H5C_notify_action_t action, void *thing);
+static herr_t H5C__epoch_marker_free_icr(void * thing);
+
+static herr_t H5C__epoch_marker_clear(const H5F_t *f, void * thing,
+ hbool_t about_to_destroy);
+static herr_t H5C__epoch_marker_fsf_size(const void H5_ATTR_UNUSED * thing,
+ size_t H5_ATTR_UNUSED * fsf_size_ptr);
const H5C_class_t epoch_marker_class =
{
- /* id = */ H5C__EPOCH_MARKER_TYPE,
- /* load = */ &H5C_epoch_marker_load,
- /* flush = */ &H5C_epoch_marker_flush,
- /* dest = */ &H5C_epoch_marker_dest,
- /* clear = */ &H5C_epoch_marker_clear,
- /* notify = */&H5C_epoch_marker_notify,
- /* size = */ &H5C_epoch_marker_size
+ /* id = */ H5C__EPOCH_MARKER_TYPE,
+ /* name = */ "epoch marker",
+ /* mem_type = */ H5FD_MEM_DEFAULT, /* value doesn't matter */
+ /* flags = */ H5AC__CLASS_NO_FLAGS_SET,
+ /* get_load_size = */ H5C__epoch_marker_get_load_size,
+ /* deserialize = */ H5C__epoch_marker_deserialize,
+ /* image_len = */ H5C__epoch_marker_image_len,
+ /* pre_serialize = */ H5C__epoch_marker_pre_serialize,
+ /* serialize = */ H5C__epoch_marker_serialize,
+ /* notify = */ H5C__epoch_marker_notify,
+ /* free_icr = */ H5C__epoch_marker_free_icr,
+ /* clear = */ H5C__epoch_marker_clear,
+ /* fsf_size = */ H5C__epoch_marker_fsf_size,
};
@@ -239,107 +275,114 @@ const H5C_class_t epoch_marker_class =
* JRM - 11/16/04
*
***************************************************************************/
-
-static void *
-H5C_epoch_marker_load(H5F_t H5_ATTR_UNUSED * f,
- hid_t H5_ATTR_UNUSED dxpl_id,
- haddr_t H5_ATTR_UNUSED addr,
- void H5_ATTR_UNUSED * udata)
+static herr_t
+H5C__epoch_marker_get_load_size(const void H5_ATTR_UNUSED *udata_ptr,
+ size_t H5_ATTR_UNUSED *image_len_ptr)
{
- void * ret_value = NULL; /* Return value */
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
- FUNC_ENTER_NOAPI_NOINIT
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "called unreachable fcn.")
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__epoch_marker_get_load_size() */
-done:
+
+static void *
+H5C__epoch_marker_deserialize(const void H5_ATTR_UNUSED * image_ptr, size_t H5_ATTR_UNUSED len,
+ void H5_ATTR_UNUSED * udata, hbool_t H5_ATTR_UNUSED * dirty_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
- FUNC_LEAVE_NOAPI(ret_value)
-}
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+ FUNC_LEAVE_NOAPI(NULL)
+} /* end H5C__epoch_marker_deserialize() */
static herr_t
-H5C_epoch_marker_flush(H5F_t H5_ATTR_UNUSED *f,
- hid_t H5_ATTR_UNUSED dxpl_id,
- hbool_t H5_ATTR_UNUSED dest,
- haddr_t H5_ATTR_UNUSED addr,
- void H5_ATTR_UNUSED *thing,
- unsigned H5_ATTR_UNUSED * flags_ptr)
+H5C__epoch_marker_image_len(const void H5_ATTR_UNUSED *thing,
+ size_t H5_ATTR_UNUSED *image_len_ptr, hbool_t H5_ATTR_UNUSED *compressed_ptr,
+ size_t H5_ATTR_UNUSED *compressed_len_ptr)
{
- herr_t ret_value = FAIL; /* Return value */
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
- FUNC_ENTER_NOAPI_NOINIT
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "called unreachable fcn.")
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__epoch_marker_image_len() */
-done:
+
+static herr_t
+H5C__epoch_marker_pre_serialize(const H5F_t H5_ATTR_UNUSED *f, hid_t H5_ATTR_UNUSED dxpl_id,
+ void H5_ATTR_UNUSED *thing, haddr_t H5_ATTR_UNUSED addr, size_t H5_ATTR_UNUSED len,
+ size_t H5_ATTR_UNUSED compressed_len, haddr_t H5_ATTR_UNUSED *new_addr_ptr,
+ size_t H5_ATTR_UNUSED *new_len_ptr, size_t H5_ATTR_UNUSED *new_compressed_len_ptr,
+ unsigned H5_ATTR_UNUSED *flags_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
- FUNC_LEAVE_NOAPI(ret_value)
-}
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__epoch_marker_pre_serialize() */
static herr_t
-H5C_epoch_marker_dest(H5F_t H5_ATTR_UNUSED * f,
- void H5_ATTR_UNUSED * thing)
+H5C__epoch_marker_serialize(const H5F_t H5_ATTR_UNUSED *f, void H5_ATTR_UNUSED *image_ptr,
+ size_t H5_ATTR_UNUSED len, void H5_ATTR_UNUSED *thing)
{
- herr_t ret_value = FAIL; /* Return value */
-
- FUNC_ENTER_NOAPI_NOINIT
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "called unreachable fcn.")
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
-done:
-
- FUNC_LEAVE_NOAPI(ret_value)
-}
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__epoch_marker_serialize() */
+
static herr_t
-H5C_epoch_marker_clear(H5F_t H5_ATTR_UNUSED * f,
- void H5_ATTR_UNUSED * thing,
- hbool_t H5_ATTR_UNUSED dest)
+H5C__epoch_marker_notify(H5C_notify_action_t H5_ATTR_UNUSED action,
+ void H5_ATTR_UNUSED * thing)
{
- herr_t ret_value = FAIL; /* Return value */
-
- FUNC_ENTER_NOAPI_NOINIT
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "called unreachable fcn.")
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
-done:
-
- FUNC_LEAVE_NOAPI(ret_value)
-}
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__epoch_marker_notify() */
+
static herr_t
-H5C_epoch_marker_notify(H5C_notify_action_t H5_ATTR_UNUSED action,
- void H5_ATTR_UNUSED * thing)
+H5C__epoch_marker_free_icr(void H5_ATTR_UNUSED * thing)
{
- herr_t ret_value = FAIL; /* Return value */
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
- FUNC_ENTER_NOAPI_NOINIT
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "called unreachable fcn.")
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__epoch_marker_free_icr() */
-done:
- FUNC_LEAVE_NOAPI(ret_value)
-}
-
-static herr_t
-H5C_epoch_marker_size(const H5F_t H5_ATTR_UNUSED * f,
- const void H5_ATTR_UNUSED * thing,
- size_t H5_ATTR_UNUSED * size_ptr)
+
+static herr_t
+H5C__epoch_marker_clear(const H5F_t H5_ATTR_UNUSED *f, void H5_ATTR_UNUSED * thing, hbool_t H5_ATTR_UNUSED about_to_destroy)
{
- herr_t ret_value = FAIL; /* Return value */
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
- FUNC_ENTER_NOAPI_NOINIT
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "called unreachable fcn.")
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__epoch_marker_clear() */
-done:
+
+static herr_t
+H5C__epoch_marker_fsf_size(const void H5_ATTR_UNUSED * thing, size_t H5_ATTR_UNUSED *fsf_size_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
+
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__epoch_marker_fsf_size() */
- FUNC_LEAVE_NOAPI(ret_value)
-}
/*-------------------------------------------------------------------------
@@ -423,21 +466,46 @@ done:
* Programmer: John Mainzer
* 3/17/10
*
+ * Changes: Ported code to detect next entry status changes as the
+ * the result of a flush from the serial code in the scan of
+ * the LRU. Also added code to detect and adapt to the
+ * removal from the cache of the next entry in the scan of
+ * the LRU.
+ *
+ * Note that at present, all of these changes should not
+ * be required as the operations on entries as they are
+ * flushed that can cause these condiditions are not premitted
+ * in the parallel case. However, Quincey indicates that
+ * this may change, and thus has requested the modification.
+ *
+ * Note the assert(FALSE) in the if statement whose body
+ * restarts the scan of the LRU. As the body of the if
+ * statement should be unreachable, it should never be
+ * triggered until the constraints on the parallel case
+ * are relaxed. Please remove the assertion at that time.
+ *
+ * Also added warning on the Pinned Entry List scan, as it
+ * is potentially subject to the same issue. As there is
+ * no cognate of this scan in the serial code, I don't have
+ * a fix to port to it.
+ *
+ * JRM -- 4/10/19
+ *
*-------------------------------------------------------------------------
*/
#ifdef H5_HAVE_PARALLEL
#define H5C_APPLY_CANDIDATE_LIST__DEBUG 0
herr_t
H5C_apply_candidate_list(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
+ hid_t dxpl_id,
H5C_t * cache_ptr,
int num_candidates,
haddr_t * candidates_list_ptr,
int mpi_rank,
int mpi_size)
{
- hbool_t first_flush = FALSE;
+ hbool_t restart_scan;
+ hbool_t prev_is_dirty;
int i;
int m;
int n;
@@ -457,6 +525,7 @@ H5C_apply_candidate_list(H5F_t * f,
int * candidate_assignment_table = NULL;
haddr_t addr;
H5C_cache_entry_t * clear_ptr = NULL;
+ H5C_cache_entry_t * next_ptr = NULL;
H5C_cache_entry_t * entry_ptr = NULL;
H5C_cache_entry_t * flush_ptr = NULL;
H5C_cache_entry_t * delayed_ptr = NULL;
@@ -634,16 +703,29 @@ H5C_apply_candidate_list(H5F_t * f,
* should be reworked to account for additional cases.
* ===================================================================== */
+ HDassert(entries_to_flush >= 0);
+
+ restart_scan = FALSE;
entries_examined = 0;
initial_list_len = cache_ptr->LRU_list_len;
entry_ptr = cache_ptr->LRU_tail_ptr;
/* Examine each entry in the LRU list */
- while((entry_ptr != NULL) && (entries_examined <= initial_list_len) &&
- ((entries_cleared + entries_flushed) < num_candidates)) {
+ while ( ( entry_ptr != NULL )
+ &&
+ ( entries_examined <= (entries_to_flush + 1) * initial_list_len )
+ &&
+ ( (entries_cleared + entries_flushed) < num_candidates ) ) {
+
+ if ( entry_ptr->prev != NULL )
+ prev_is_dirty = entry_ptr->prev->is_dirty;
/* If this process needs to clear this entry. */
if(entry_ptr->clear_on_unprotect) {
+
+ HDassert(entry_ptr->is_dirty);
+
+ next_ptr = entry_ptr->next;
entry_ptr->clear_on_unprotect = FALSE;
clear_ptr = entry_ptr;
entry_ptr = entry_ptr->prev;
@@ -654,20 +736,26 @@ H5C_apply_candidate_list(H5F_t * f,
(long long)clear_ptr->addr);
#endif /* H5C_APPLY_CANDIDATE_LIST__DEBUG */
+ /* No need to check for the next entry in the scan being
+ * removed from the cache, as this call to H5C_flush_single_entry()
+ * will not call either the pre_serialize or serialize callbacks.
+ */
+
if(H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- clear_ptr->type,
+ dxpl_id,
clear_ptr->addr,
H5C__FLUSH_CLEAR_ONLY_FLAG,
- &first_flush,
- TRUE) < 0)
+ TRUE,
+ NULL) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.")
} /* end if */
/* Else, if this process needs to flush this entry. */
else if (entry_ptr->flush_immediately) {
+ HDassert(entry_ptr->is_dirty);
+
+ next_ptr = entry_ptr->next;
entry_ptr->flush_immediately = FALSE;
flush_ptr = entry_ptr;
entry_ptr = entry_ptr->prev;
@@ -678,22 +766,94 @@ H5C_apply_candidate_list(H5F_t * f,
(long long)flush_ptr->addr);
#endif /* H5C_APPLY_CANDIDATE_LIST__DEBUG */
+ /* 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.
+ *
+ * Note that as of this writing (April 2015) this
+ * case cannot occur in the parallel case. However
+ * Quincey is making noises about changing this, hence
+ * the insertion of this test.
+ *
+ * Note also that there is no test code to verify
+ * that this code actually works (although similar code
+ * in the serial version exists and is tested).
+ *
+ * Implementing a test will likely require implementing
+ * flush op like facilities in the parallel tests. At
+ * a guess this will not be terribly painful, but it
+ * will take a bit of time.
+ */
+ cache_ptr->entries_removed_counter = 0;
+ cache_ptr->last_entry_removed_ptr = NULL;
+
if(H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- flush_ptr->type,
+ dxpl_id,
flush_ptr->addr,
H5C__NO_FLAGS_SET,
- &first_flush,
- TRUE) < 0)
+ TRUE,
+ NULL) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't flush entry.")
+
+ if ( ( cache_ptr->entries_removed_counter > 1 ) ||
+ ( cache_ptr->last_entry_removed_ptr == entry_ptr ) )
+
+ restart_scan = TRUE;
+
} /* end else-if */
/* Otherwise, no action to be taken on this entry. Grab the next. */
else {
entry_ptr = entry_ptr->prev;
+
+ if ( entry_ptr != NULL )
+ next_ptr = entry_ptr->next;
+
} /* end else */
+ if ( ( entry_ptr != NULL )
+ &&
+ ( ( restart_scan )
+ ||
+ ( entry_ptr->is_dirty != prev_is_dirty )
+ ||
+ ( entry_ptr->next != next_ptr )
+ ||
+ ( entry_ptr->is_protected )
+ ||
+ ( entry_ptr->is_pinned )
+ )
+ ) {
+
+ /* something has happened to the LRU -- start over
+ * from the tail.
+ *
+ * Recall that this code should be un-reachable at present,
+ * as all the operations by entries on flush that could cause
+ * it to be reachable are disallowed in the parallel case at
+ * present. Hence the following assertion which should be
+ * removed if the above changes.
+ */
+
+ HDassert( ! restart_scan );
+ HDassert( entry_ptr->is_dirty == prev_is_dirty );
+ HDassert( entry_ptr->next == next_ptr );
+ HDassert( ! entry_ptr->is_protected );
+ HDassert( ! entry_ptr->is_pinned );
+
+ HDassert(FALSE); /* see comment above */
+
+ restart_scan = FALSE;
+ entry_ptr = cache_ptr->LRU_tail_ptr;
+/*
+ H5C__UPDATE_STATS_FOR_LRU_SCAN_RESTART(cache_ptr)
+*/
+ }
+
entries_examined++;
} /* end while */
@@ -705,6 +865,30 @@ H5C_apply_candidate_list(H5F_t * f,
/* It is also possible that some of the cleared entries are on the
* pinned list. Must scan that also.
+ *
+ * WARNING:
+ *
+ * As we now allow unpinning, and removal of other entries as a side
+ * effect of flushing an entry, it is possible that the next entry
+ * in a PEL scan could either be no longer pinned, or no longer in
+ * the cache by the time we get to it.
+ *
+ * At present, this is not possible in this case, as we disallow such
+ * operations in the parallel version of the library. However, Quincey
+ * has been making noises about relaxing this. If and when he does,
+ * we have a potential problem here.
+ *
+ * The same issue exists in the serial cache, and there are tests
+ * to detect this problem when it occurs, and adjust to it. As seen
+ * above in the LRU scan, I have ported such tests to the parallel
+ * code where a close cognate exists in the serial code.
+ *
+ * I haven't done so here, as there are no PEL scans where the problem
+ * can occur in the serial code. Needless to say, this will have to
+ * be repaired if the constraints on pre_serialize and serialize
+ * callbacks are relaxed in the parallel version of the metadata cache.
+ *
+ * JRM -- 4/1/15
*/
#if H5C_APPLY_CANDIDATE_LIST__DEBUG
@@ -772,14 +956,12 @@ H5C_apply_candidate_list(H5F_t * f,
#endif /* H5C_APPLY_CANDIDATE_LIST__DEBUG */
if(H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- clear_ptr->type,
- clear_ptr->addr,
- H5C__FLUSH_CLEAR_ONLY_FLAG,
- &first_flush,
- TRUE) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.")
+ dxpl_id,
+ clear_ptr->addr,
+ H5C__FLUSH_CLEAR_ONLY_FLAG,
+ TRUE,
+ NULL) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.")
} /* end else-if */
/* Else, if this process needs to independently flush this entry. */
@@ -795,14 +977,12 @@ H5C_apply_candidate_list(H5F_t * f,
#endif /* H5C_APPLY_CANDIDATE_LIST__DEBUG */
if(H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- flush_ptr->type,
- flush_ptr->addr,
- H5C__NO_FLAGS_SET,
- &first_flush,
- TRUE) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't flush entry.")
+ dxpl_id,
+ flush_ptr->addr,
+ H5C__NO_FLAGS_SET,
+ TRUE,
+ NULL) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.")
} /* end else-if */
} /* end if */
@@ -843,13 +1023,11 @@ H5C_apply_candidate_list(H5F_t * f,
} /* end if */
if(H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- delayed_ptr->type,
+ dxpl_id,
delayed_ptr->addr,
H5C__NO_FLAGS_SET,
- &first_flush,
- TRUE) < 0)
+ TRUE,
+ NULL) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL,
"Can't flush entry collectively.")
@@ -1184,6 +1362,9 @@ H5C_create(size_t max_cache_size,
/* Tagging Field Initializations */
cache_ptr->ignore_tags = FALSE;
+ cache_ptr->slist_changed = FALSE;
+ cache_ptr->slist_change_in_pre_serialize = FALSE;
+ cache_ptr->slist_change_in_serialize = FALSE;
cache_ptr->slist_len = 0;
cache_ptr->slist_size = (size_t)0;
@@ -1197,6 +1378,9 @@ H5C_create(size_t max_cache_size,
(cache_ptr->index)[i] = NULL;
}
+ cache_ptr->entries_removed_counter = 0;
+ cache_ptr->last_entry_removed_ptr = NULL;
+
cache_ptr->pl_len = 0;
cache_ptr->pl_size = (size_t)0;
cache_ptr->pl_head_ptr = NULL;
@@ -1271,11 +1455,8 @@ H5C_create(size_t max_cache_size,
/* Set non-zero/FALSE/NULL fields for epoch markers */
for ( i = 0; i < H5C__MAX_EPOCH_MARKERS; i++ )
{
- (cache_ptr->epoch_marker_active)[i] = FALSE;
-#ifndef NDEBUG
((cache_ptr->epoch_markers)[i]).magic =
H5C__H5C_CACHE_ENTRY_T_MAGIC;
-#endif /* NDEBUG */
((cache_ptr->epoch_markers)[i]).addr = (haddr_t)i;
((cache_ptr->epoch_markers)[i]).type = &epoch_marker_class;
}
@@ -1530,9 +1711,7 @@ H5C_def_auto_resize_rpt_fcn(H5C_t * cache_ptr,
*-------------------------------------------------------------------------
*/
herr_t
-H5C_dest(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id)
+H5C_dest(H5F_t * f, hid_t dxpl_id)
{
H5C_t * cache_ptr = f->shared->cache;
herr_t ret_value = SUCCEED; /* Return value */
@@ -1544,8 +1723,7 @@ H5C_dest(H5F_t * f,
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
/* Flush and invalidate all cache entries */
- if(H5C_flush_invalidate_cache(f, primary_dxpl_id, secondary_dxpl_id,
- H5C__NO_FLAGS_SET) < 0 )
+ if(H5C_flush_invalidate_cache(f, dxpl_id, H5C__NO_FLAGS_SET) < 0 )
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache")
if(cache_ptr->slist_ptr != NULL) {
@@ -1570,7 +1748,9 @@ H5C_dest(H5F_t * f,
#endif /* H5C_DO_SANITY_CHECKS */
#endif /* NDEBUG */
+#ifndef NDEBUG
cache_ptr->magic = 0;
+#endif /* NDEBUG */
cache_ptr = H5FL_FREE(H5C_t, cache_ptr);
@@ -1595,17 +1775,16 @@ done:
*-------------------------------------------------------------------------
*/
herr_t
-H5C_expunge_entry(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- const H5C_class_t * type,
- haddr_t addr,
- unsigned flags)
+H5C_expunge_entry(H5F_t *f, hid_t dxpl_id, const H5C_class_t *type,
+ haddr_t addr, unsigned flags)
{
H5C_t * cache_ptr;
- herr_t result;
- hbool_t first_flush = TRUE;
H5C_cache_entry_t * entry_ptr = NULL;
+ unsigned flush_flags = (H5C__FLUSH_INVALIDATE_FLAG | H5C__FLUSH_CLEAR_ONLY_FLAG);
+#if H5C_DO_SANITY_CHECKS
+ hbool_t entry_was_dirty;
+ hsize_t entry_size;
+#endif /* H5C_DO_SANITY_CHECKS */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
@@ -1616,8 +1795,6 @@ H5C_expunge_entry(H5F_t * f,
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(type);
- HDassert(type->clear);
- HDassert(type->dest);
HDassert(H5F_addr_defined(addr));
#if H5C_DO_EXTREME_SANITY_CHECKS
@@ -1641,27 +1818,34 @@ H5C_expunge_entry(H5F_t * f,
if(entry_ptr->is_pinned)
HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, "Target entry is pinned.")
- /* Pass along 'free file space' flag to cache client */
- entry_ptr->free_file_space_on_destroy = ( (flags & H5C__FREE_FILE_SPACE_FLAG) != 0 );
-
/* 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.
*/
- result = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- entry_ptr->type,
- entry_ptr->addr,
- H5C__FLUSH_INVALIDATE_FLAG | H5C__FLUSH_CLEAR_ONLY_FLAG,
- &first_flush,
- TRUE);
- if ( result < 0 ) {
+ /* Pass along 'free file space' flag to cache client. */
+ flush_flags |= (flags & H5C__FREE_FILE_SPACE_FLAG);
- HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, \
- "H5C_flush_single_entry() failed.")
+#if H5C_DO_SANITY_CHECKS
+ entry_was_dirty = entry_ptr->is_dirty;
+ entry_size = entry_ptr->size;
+#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
+
+ if(H5C_flush_single_entry(f, dxpl_id, entry_ptr->addr, flush_flags, TRUE, NULL) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, "H5C_flush_single_entry() failed.")
+
+#if H5C_DO_SANITY_CHECKS
+ if ( entry_was_dirty )
+ {
+ /* we have just removed an entry from the skip list. Thus
+ * we must touch up cache_ptr->slist_len_increase and
+ * cache_ptr->slist_size_increase to keep from skewing
+ * the sanity checks on flushes.
+ */
+ cache_ptr->slist_len_increase -= 1;
+ cache_ptr->slist_size_increase -= (int64_t)(entry_size);
}
+#endif /* H5C_DO_SANITY_CHECKS */
done:
#if H5C_DO_EXTREME_SANITY_CHECKS
@@ -1698,10 +1882,24 @@ done:
* 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
+ *
*-------------------------------------------------------------------------
*/
herr_t
-H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsigned flags)
+H5C_flush_cache(H5F_t *f, hid_t dxpl_id, unsigned flags)
{
H5C_t * cache_ptr = f->shared->cache;
herr_t status;
@@ -1709,9 +1907,9 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
hbool_t destroy;
hbool_t flushed_entries_last_pass;
hbool_t flush_marked_entries;
- hbool_t first_flush = TRUE;
hbool_t ignore_protected;
hbool_t tried_to_flush_protected_entry = FALSE;
+ hbool_t restart_slist_scan;
int32_t passes = 0;
int32_t protected_entries = 0;
H5SL_node_t * node_ptr = NULL;
@@ -1722,6 +1920,10 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
int64_t flushed_entries_size = 0;
int64_t initial_slist_len = 0;
size_t initial_slist_size = 0;
+ int64_t entry_size_change;
+ int64_t * entry_size_change_ptr = &entry_size_change;
+#else /* H5C_DO_SANITY_CHECKS */
+ int64_t * entry_size_change_ptr = NULL;
#endif /* H5C_DO_SANITY_CHECKS */
FUNC_ENTER_NOAPI(FAIL)
@@ -1758,10 +1960,7 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
if ( destroy ) {
- status = H5C_flush_invalidate_cache(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- flags);
+ status = H5C_flush_invalidate_cache(f, dxpl_id, flags);
if ( status < 0 ) {
@@ -1780,6 +1979,18 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
flushed_entries_last_pass = TRUE;
+ /* set the cache_ptr->slist_change_in_pre_serialize and
+ * cache_ptr->slist_change_in_serialize to false.
+ *
+ * These flags are set to TRUE by H5C_flush_single_entry if the
+ * slist is modified by a pre_serialize or serialize call respectively.
+ * H5C_flush_cache uses these flags 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_change_in_pre_serialize = FALSE;
+ cache_ptr->slist_change_in_serialize = FALSE;
+
while ( ( passes < H5C__MAX_PASSES_ON_FLUSH ) &&
( cache_ptr->slist_len != 0 ) &&
( protected_entries == 0 ) &&
@@ -1797,18 +2008,6 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
{
hbool_t flushed_during_dep_loop = FALSE;
- /* Start at beginning of skip list each time */
- node_ptr = H5SL_first(cache_ptr->slist_ptr);
- HDassert( node_ptr != NULL );
-
- /* 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 );
-
#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
@@ -1818,8 +2017,10 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
* or may not flush all the entries in the slist.
*
* To make things more entertaining, with the advent of the
- * fractal heap, the entry flush callback can cause entries
- * to be dirtied, resized, and/or moved.
+ * 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:
@@ -1834,7 +2035,8 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
flushed_entries_size = 0;
/* As mentioned above, there is the possibility that
- * entries will be dirtied, resized, and/or flushed during
+ * 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
@@ -1851,53 +2053,60 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
*/
#endif /* H5C_DO_SANITY_CHECKS */
- while ( node_ptr != NULL )
+ restart_slist_scan = TRUE;
+
+ while ( ( restart_slist_scan ) || ( node_ptr != NULL ) )
{
- entry_ptr = next_entry_ptr;
+ if ( restart_slist_scan )
+ {
+ restart_slist_scan = FALSE;
- /* With the advent of the fractal heap, it is possible
- * that the flush callback will dirty and/or resize
- * other entries in the cache. In particular, while
- * Quincey has promised me that this will never happen,
- * it is possible that the flush callback for an
- * entry may protect an entry that is not in the cache,
- * perhaps causing the cache to flush and possibly
- * evict the entry associated with node_ptr to make
- * space for the new entry.
- *
- * Thus we do a bit of extra sanity checking on entry_ptr,
- * and break out of this scan of the skip list if we
- * detect minor problems. We have a bit of leaway on the
- * number of passes though the skip list, so this shouldn't
- * be an issue in the flush in and of itself, as it should
- * be all but impossible for this to happen more than once
- * in any flush.
- *
- * Observe that that breaking out of the scan early
- * shouldn't break the sanity checks just after the end
- * of this while loop.
- *
- * If an entry has merely been marked clean and removed from
- * the s-list, we simply break out of the scan.
- *
- * If the entry has been evicted, we flag an error and
- * exit.
- */
-#ifndef NDEBUG
- if ( entry_ptr->magic != H5C__H5C_CACHE_ENTRY_T_MAGIC ) {
+ /* Start at beginning of skip list */
+ node_ptr = H5SL_first(cache_ptr->slist_ptr);
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "entry_ptr->magic is invalid ?!?!")
+ if ( node_ptr == NULL )
+ {
+ /* the slist is empty -- break out of inner loop */
+ break;
+ }
+ HDassert( node_ptr != NULL );
- } else
-#endif /* NDEBUG */
- if ( ( ! entry_ptr->is_dirty ) ||
- ( ! entry_ptr->in_slist ) ) {
+ /* Get cache entry for this node */
+ next_entry_ptr =
+ (H5C_cache_entry_t *)H5SL_item(node_ptr);
- /* the s-list has been modified out from under us.
- * break out of the loop.
- */
- goto end_of_inner_loop;;
+ 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 );
}
+
+ 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);
/* increment node pointer now, before we delete its target
* from the slist.
@@ -1911,6 +2120,7 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
HDassert( next_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC );
HDassert( next_entry_ptr->is_dirty );
HDassert( next_entry_ptr->in_slist );
+ HDassert( entry_ptr != next_entry_ptr );
} else {
next_entry_ptr = NULL;
}
@@ -1927,9 +2137,9 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
if ( entry_ptr->is_protected ) {
- /* we probably have major problems -- but lets flush
- * everything we can before we decide whether to flag
- * an error.
+ /* 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++;
@@ -1944,15 +2154,14 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
#if H5C_DO_SANITY_CHECKS
flushed_entries_count++;
flushed_entries_size += (int64_t)entry_ptr->size;
+ entry_size_change = 0;
#endif /* H5C_DO_SANITY_CHECKS */
status = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- NULL,
- entry_ptr->addr,
- flags,
- &first_flush,
- FALSE);
+ dxpl_id,
+ entry_ptr->addr,
+ flags,
+ FALSE,
+ entry_size_change_ptr);
if ( status < 0 ) {
/* This shouldn't happen -- if it does,
@@ -1961,7 +2170,35 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
"dirty pinned entry flush failed.")
} /* end if */
+
+#if H5C_DO_SANITY_CHECKS
+ /* it is possible that the entry size changed
+ * during flush -- update flushed_entries_size
+ * to account for this.
+ */
+ flushed_entries_size += entry_size_change;
+#endif /* H5C_DO_SANITY_CHECKS */
+
flushed_during_dep_loop = TRUE;
+
+ if ((cache_ptr->slist_change_in_serialize) ||
+ (cache_ptr->slist_change_in_pre_serialize))
+ {
+ /* 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_change_in_pre_serialize
+ = FALSE;
+ cache_ptr->slist_change_in_serialize
+ = FALSE;
+
+ H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr)
+ }
} /* end if */
else if(entry_ptr->flush_dep_height < curr_flush_dep_height)
/* This shouldn't happen -- if it does, just scream and die. */
@@ -1977,15 +2214,14 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
#if H5C_DO_SANITY_CHECKS
flushed_entries_count++;
flushed_entries_size += (int64_t)entry_ptr->size;
+ entry_size_change = 0;
#endif /* H5C_DO_SANITY_CHECKS */
status = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- NULL,
- entry_ptr->addr,
- flags,
- &first_flush,
- FALSE);
+ dxpl_id,
+ entry_ptr->addr,
+ flags,
+ FALSE,
+ entry_size_change_ptr);
if ( status < 0 ) {
/* This shouldn't happen -- if it does,
@@ -1994,14 +2230,42 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
"Can't flush entry.")
}
+
+#if H5C_DO_SANITY_CHECKS
+ /* it is possible that the entry size changed
+ * during flush -- update flushed_entries_size
+ * to account for this.
+ */
+ flushed_entries_size += entry_size_change;
+#endif /* H5C_DO_SANITY_CHECKS */
+
flushed_during_dep_loop = TRUE;
+
+ if ((cache_ptr->slist_change_in_serialize) ||
+ (cache_ptr->slist_change_in_pre_serialize))
+ {
+ /* 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_change_in_pre_serialize
+ = FALSE;
+ cache_ptr->slist_change_in_serialize
+ = FALSE;
+
+ H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr)
+ }
} /* end if */
else if(entry_ptr->flush_dep_height < curr_flush_dep_height)
/* This shouldn't happen -- if it does, just scream and die. */
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "dirty entry below current flush dep. height.")
} /* end else */
} /* end if */
- } /* while ( node_ptr != NULL ) */
+ } /* while ( ( restart_slist_scan ) || ( node_ptr != NULL ) ) */
/* Check for incrementing flush dependency height */
if(flushed_during_dep_loop) {
@@ -2020,7 +2284,8 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign
curr_flush_dep_height++;
} /* while ( curr_flush_dep_height <= H5C__NUM_FLUSH_DEP_HEIGHTS) */
-end_of_inner_loop:
+
+ passes++;
#if H5C_DO_SANITY_CHECKS
/* Verify that the slist size and length are as expected. */
@@ -2032,8 +2297,6 @@ end_of_inner_loop:
flushed_entries_size) == cache_ptr->slist_size );
#endif /* H5C_DO_SANITY_CHECKS */
- passes++;
-
} /* while */
HDassert( protected_entries <= cache_ptr->pl_len );
@@ -2098,12 +2361,10 @@ done:
*/
herr_t
H5C_flush_to_min_clean(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id)
+ hid_t dxpl_id)
{
H5C_t * cache_ptr;
herr_t result;
- hbool_t first_flush = TRUE;
hbool_t write_permitted;
#if 0 /* modified code -- commented out for now */
int i;
@@ -2146,11 +2407,9 @@ H5C_flush_to_min_clean(H5F_t * f,
}
#if 1 /* original code */
result = H5C_make_space_in_cache(f,
- primary_dxpl_id,
- secondary_dxpl_id,
+ dxpl_id,
(size_t)0,
- write_permitted,
- &first_flush);
+ write_permitted);
if ( result < 0 ) {
@@ -2645,8 +2904,7 @@ H5C_get_trace_file_ptr_from_entry(const H5C_cache_entry_t *entry_ptr)
*/
herr_t
H5C_insert_entry(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
+ hid_t dxpl_id,
const H5C_class_t * type,
haddr_t addr,
void * thing,
@@ -2654,7 +2912,6 @@ H5C_insert_entry(H5F_t * f,
{
H5C_t * cache_ptr;
herr_t result;
- hbool_t first_flush = TRUE;
hbool_t insert_pinned;
hbool_t flush_last;
#ifdef H5_HAVE_PARALLEL
@@ -2678,8 +2935,6 @@ H5C_insert_entry(H5F_t * f,
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( type );
- HDassert( type->flush );
- HDassert( type->size );
HDassert( H5F_addr_defined(addr) );
HDassert( thing );
@@ -2718,15 +2973,16 @@ H5C_insert_entry(H5F_t * f,
HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "duplicate entry in cache.")
} /* end if */
-#ifndef NDEBUG
entry_ptr->magic = H5C__H5C_CACHE_ENTRY_T_MAGIC;
-#endif /* NDEBUG */
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;
+
/* Apply tag to newly inserted entry */
- if(H5C_tag_entry(cache_ptr, entry_ptr, primary_dxpl_id) < 0)
+ if(H5C_tag_entry(cache_ptr, entry_ptr, dxpl_id) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "Cannot tag metadata entry")
entry_ptr->is_protected = FALSE;
@@ -2747,10 +3003,27 @@ H5C_insert_entry(H5F_t * f,
/* not protected, so can't be dirtied */
entry_ptr->dirtied = FALSE;
- /* Retrieve the size of the thing */
- if((type->size)(f, thing, &(entry_ptr->size)) < 0)
+ /* Retrieve the size of the thing. Set the compressed field to FALSE
+ * and the compressed_size field to zero first, as they may not be
+ * initialized by the image_len call.
+ */
+ entry_ptr->compressed = FALSE;
+ entry_ptr->compressed_size = 0;
+ if((type->image_len)(thing, &(entry_ptr->size), &(entry_ptr->compressed),
+ &(entry_ptr->compressed_size)) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, FAIL, "Can't get size of thing")
HDassert(entry_ptr->size > 0 && entry_ptr->size < H5C_MAX_ENTRY_SIZE);
+ HDassert(((type->flags & H5C__CLASS_COMPRESSED_FLAG) != 0) ||
+ (entry_ptr->compressed == FALSE));
+
+ /* entry has just been inserted -- thus compressed size cannot have
+ * been computed yet. Thus if entry_ptr->compressed is TRUE,
+ * entry_ptr->size must equal entry_ptr->compressed_size.
+ */
+ HDassert((entry_ptr->compressed == FALSE) ||
+ (entry_ptr->size == entry_ptr->compressed_size));
+ HDassert((entry_ptr->compressed == TRUE) ||
+ (entry_ptr->compressed_size == 0));
entry_ptr->in_slist = FALSE;
@@ -2761,7 +3034,6 @@ H5C_insert_entry(H5F_t * f,
entry_ptr->flush_in_progress = FALSE;
entry_ptr->destroy_in_progress = FALSE;
- entry_ptr->free_file_space_on_destroy = FALSE;
/* Initialize flush dependency height fields */
entry_ptr->flush_dep_parent = NULL;
@@ -2860,11 +3132,9 @@ H5C_insert_entry(H5F_t * f,
*/
result = H5C_make_space_in_cache(f,
- primary_dxpl_id,
- secondary_dxpl_id,
+ dxpl_id,
space_needed,
- write_permitted,
- &first_flush);
+ write_permitted);
if ( result < 0 ) {
@@ -2956,18 +3226,39 @@ done:
* Programmer: John Mainzer
* 7/5/05
*
+ * Changes: Tidied up code, removeing some old commented out
+ * code that had been left in pending success of the
+ * new version.
+ *
+ * Note that unlike H5C_apply_candidate_list(),
+ * H5C_mark_entries_as_clean() makes all its calls to
+ * H6C_flush_single_entry() with the
+ * H5C__FLUSH_CLEAR_ONLY_FLAG set. As a result,
+ * the pre_serialize() and serialize calls are not made.
+ *
+ * This then implies that (assuming such actions were
+ * permitted in the parallel case) no loads, dirties,
+ * resizes, or removals of other entries can occur as
+ * a side effect of the flush. Hence, there is no need
+ * for the checks for entry removal / status change
+ * that I ported to H5C_apply_candidate_list().
+ *
+ * However, if (in addition to allowing such operations
+ * in the parallel case), we allow such operations outside
+ * of the pre_serialize / serialize routines, this may
+ * cease to be the case -- requiring a review of this
+ * function.
+ *
*-------------------------------------------------------------------------
*/
#ifdef H5_HAVE_PARALLEL
herr_t
H5C_mark_entries_as_clean(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
+ hid_t dxpl_id,
int32_t ce_array_len,
haddr_t * ce_array_ptr)
{
H5C_t * cache_ptr;
- hbool_t first_flush = TRUE;
int entries_cleared;
int entries_examined;
int i;
@@ -3061,27 +3352,9 @@ H5C_mark_entries_as_clean(H5F_t * f,
#endif /* H5C_DO_SANITY_CHECKS */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
"Listed entry not dirty?!?!?.")
-#if 0 /* original code */
- } else if ( entry_ptr->is_protected ) {
-
- entry_ptr->clear_on_unprotect = TRUE;
} else {
- if ( H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- entry_ptr->type,
- addr,
- H5C__FLUSH_CLEAR_ONLY_FLAG,
- &first_flush,
- TRUE) < 0 ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.")
- }
- }
-#else /* modified code */
- } else {
/* Mark the entry to be cleared on unprotect. We will
* scan the LRU list shortly, and clear all those entries
* not currently protected.
@@ -3102,13 +3375,32 @@ H5C_mark_entries_as_clean(H5F_t * f,
}
#endif /* H5C_DO_SANITY_CHECKS */
}
-#endif /* end modified code */
}
-#if 1 /* modified code */
+
/* Scan through the LRU list from back to front, and flush the
* entries whose clear_on_unprotect flags are set. Observe that
* any protected entries will not be on the LRU, and therefore
* will not be flushed at this time.
+ *
+ * Note that unlike H5C_apply_candidate_list(),
+ * H5C_mark_entries_as_clean() makes all its calls to
+ * H6C_flush_single_entry() with the H5C__FLUSH_CLEAR_ONLY_FLAG
+ * set. As a result, the pre_serialize() and serialize calls are
+ * not made.
+ *
+ * This then implies that (assuming such actions were
+ * permitted in the parallel case) no loads, dirties,
+ * resizes, or removals of other entries can occur as
+ * a side effect of the flush. Hence, there is no need
+ * for the checks for entry removal / status change
+ * that I ported to H5C_apply_candidate_list().
+ *
+ * However, if (in addition to allowing such operations
+ * in the parallel case), we allow such operations outside
+ * of the pre_serialize / serialize routines, this may
+ * cease to be the case -- requiring a review of this
+ * point.
+ * JRM -- 4/7/15
*/
entries_cleared = 0;
@@ -3128,13 +3420,11 @@ H5C_mark_entries_as_clean(H5F_t * f,
entries_cleared++;
if ( H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- clear_ptr->type,
+ dxpl_id,
clear_ptr->addr,
H5C__FLUSH_CLEAR_ONLY_FLAG,
- &first_flush,
- TRUE) < 0 ) {
+ TRUE,
+ NULL) < 0 ) {
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.")
}
@@ -3165,13 +3455,11 @@ H5C_mark_entries_as_clean(H5F_t * f,
entries_cleared++;
if ( H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- clear_ptr->type,
+ dxpl_id,
clear_ptr->addr,
H5C__FLUSH_CLEAR_ONLY_FLAG,
- &first_flush,
- TRUE) < 0 ) {
+ TRUE,
+ NULL) < 0 ) {
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.")
}
@@ -3202,7 +3490,6 @@ H5C_mark_entries_as_clean(H5F_t * f,
}
HDassert( (entries_cleared + i) == ce_array_len );
#endif /* H5C_DO_SANITY_CHECKS */
-#endif /* modified code */
done:
@@ -3278,6 +3565,7 @@ H5C_mark_entry_dirty(void *thing)
/* mark the entry as dirty if it isn't already */
entry_ptr->is_dirty = TRUE;
+ entry_ptr->image_up_to_date = FALSE;
if ( was_pinned_unprotected_and_clean ) {
@@ -3427,36 +3715,32 @@ H5C_move_entry(H5C_t * cache_ptr,
was_dirty = entry_ptr->is_dirty;
#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
- if ( ! ( entry_ptr->flush_in_progress ) ) {
-
- entry_ptr->is_dirty = TRUE;
- }
+ entry_ptr->is_dirty = TRUE;
+ /* This shouldn't be needed, but it keeps the test code happy */
+ entry_ptr->image_up_to_date = FALSE;
H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL)
- if ( ! ( entry_ptr->flush_in_progress ) ) {
-
- H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
+ H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
#if H5C_DO_SANITY_CHECKS
- if ( removed_entry_from_slist ) {
-
- /* we just removed the entry from the slist. Thus we
- * must touch up cache_ptr->slist_len_increase and
- * cache_ptr->slist_size_increase to keep from skewing
- * the sanity checks.
- */
- HDassert( cache_ptr->slist_len_increase > 1 );
- HDassert( cache_ptr->slist_size_increase >
- (int64_t)(entry_ptr->size) );
+ if ( removed_entry_from_slist ) {
- cache_ptr->slist_len_increase -= 1;
- cache_ptr->slist_size_increase -= (int64_t)(entry_ptr->size);
- }
+ /* we just removed the entry from the slist. Thus we
+ * must touch up cache_ptr->slist_len_increase and
+ * cache_ptr->slist_size_increase to keep from skewing
+ * the sanity checks.
+ */
+ cache_ptr->slist_len_increase -= 1;
+ cache_ptr->slist_size_increase -= (int64_t)(entry_ptr->size);
+ }
#endif /* H5C_DO_SANITY_CHECKS */
+ if ( ! ( entry_ptr->flush_in_progress ) ) {
+
+ /* skip the update if a flush is in progress */
H5C__UPDATE_RP_FOR_MOVE(cache_ptr, entry_ptr, was_dirty, FAIL)
}
}
@@ -3536,6 +3820,11 @@ H5C_resize_entry(void *thing, size_t new_size)
/* mark the entry as dirty if it isn't already */
entry_ptr->is_dirty = TRUE;
+ entry_ptr->image_up_to_date = FALSE;
+
+ /* 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 ) {
@@ -3743,18 +4032,6 @@ done:
* or flushed -- nor may it be accessed by another call to
* H5C_protect. Any attempt to do so will result in a failure.
*
- * The primary_dxpl_id and secondary_dxpl_id parameters
- * specify the dxpl_ids used on the first write occasioned
- * by the insertion (primary_dxpl_id), and on all subsequent
- * writes (secondary_dxpl_id). This is useful in the
- * metadata cache, but may not be needed elsewhere. If so,
- * just use the same dxpl_id for both parameters.
- *
- * All reads are performed with the primary_dxpl_id.
- *
- * Similarly, the primary_dxpl_id is passed to the
- * check_write_permitted function if it is called.
- *
* Return: Success: Ptr to the desired entry
* Failure: NULL
*
@@ -3776,12 +4053,21 @@ done:
* entries long before we actually have to evict something
* to make space.
*
+ * JRM -- 9/1/14
+ * Replace the old rw parameter with the flags parameter.
+ * This allows H5C_protect to accept flags other than
+ * H5C__READ_ONLY_FLAG.
+ *
+ * Added support for the H5C__FLUSH_LAST_FLAG and
+ * H5C__FLUSH_COLLECTIVELY_FLAG flags. At present, these
+ * flags are only applied if the entry is not in cache, and
+ * is loaded into the cache as a result of this call.
+ *
*-------------------------------------------------------------------------
*/
void *
H5C_protect(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
+ hid_t dxpl_id,
const H5C_class_t * type,
haddr_t addr,
void * udata,
@@ -3789,9 +4075,12 @@ H5C_protect(H5F_t * f,
{
H5C_t * cache_ptr;
hbool_t hit;
- hbool_t first_flush;
hbool_t have_write_permitted = FALSE;
hbool_t read_only = FALSE;
+ hbool_t flush_last;
+#ifdef H5_HAVE_PARALLEL
+ hbool_t flush_collectively;
+#endif /* H5_HAVE_PARALLEL */
hbool_t write_permitted;
herr_t result;
size_t empty_space;
@@ -3812,8 +4101,6 @@ H5C_protect(H5F_t * f,
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( type );
- HDassert( type->flush );
- HDassert( type->load );
HDassert( H5F_addr_defined(addr) );
#if H5C_DO_EXTREME_SANITY_CHECKS
@@ -3827,6 +4114,10 @@ H5C_protect(H5F_t * f,
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
read_only = ( (flags & H5C__READ_ONLY_FLAG) != 0 );
+ flush_last = ( (flags & H5C__FLUSH_LAST_FLAG) != 0 );
+#ifdef H5_HAVE_PARALLEL
+ flush_collectively = ( (flags & H5C__FLUSH_COLLECTIVELY_FLAG) != 0 );
+#endif /* H5_HAVE_PARALLEL */
/* first check to see if the target is in cache */
H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, NULL)
@@ -3845,7 +4136,7 @@ H5C_protect(H5F_t * f,
from disk. */
/* Get the dataset transfer property list */
- if(NULL == (dxpl = (H5P_genplist_t *)H5I_object_verify(primary_dxpl_id, H5I_GENPROP_LST)))
+ if(NULL == (dxpl = (H5P_genplist_t *)H5I_object_verify(dxpl_id, H5I_GENPROP_LST)))
HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a property list");
/* Get the tag from the DXPL */
@@ -3871,7 +4162,7 @@ H5C_protect(H5F_t * f,
hit = FALSE;
- thing = H5C_load_entry(f, primary_dxpl_id, type, addr, udata);
+ thing = H5C_load_entry(f, dxpl_id, type, addr, udata);
if ( thing == NULL ) {
@@ -3881,7 +4172,7 @@ H5C_protect(H5F_t * f,
entry_ptr = (H5C_cache_entry_t *)thing;
/* Apply tag to newly protected entry */
- if(H5C_tag_entry(cache_ptr, entry_ptr, primary_dxpl_id) < 0)
+ if(H5C_tag_entry(cache_ptr, entry_ptr, dxpl_id) < 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,
@@ -3936,7 +4227,6 @@ H5C_protect(H5F_t * f,
have_write_permitted = TRUE;
- first_flush = TRUE;
}
} else {
@@ -3944,7 +4234,6 @@ H5C_protect(H5F_t * f,
have_write_permitted = TRUE;
- first_flush = TRUE;
}
HDassert( entry_ptr->size <= H5C_MAX_ENTRY_SIZE );
@@ -3985,10 +4274,10 @@ H5C_protect(H5F_t * f,
* see no point in worrying about the fourth.
*/
- result = H5C_make_space_in_cache(f, primary_dxpl_id,
- secondary_dxpl_id,
- space_needed, write_permitted,
- &first_flush);
+ result = H5C_make_space_in_cache(f,
+ dxpl_id,
+ space_needed,
+ write_permitted);
if ( result < 0 ) {
@@ -4002,7 +4291,24 @@ H5C_protect(H5F_t * f,
*
* This is no longer true -- due to a bug fix, we may modify
* data on load to repair a file.
+ *
+ * *******************************************
+ *
+ * Set the flush_last (and possibly flush_collectively) fields
+ * 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 and
+ * H5C__FLUSH_COLLECTIVELY_FLAG flags are ignored if the
+ * entry is already in cache.
*/
+ entry_ptr->flush_me_last = flush_last;
+#ifdef H5_HAVE_PARALLEL
+ entry_ptr->flush_me_collectively = flush_collectively;
+#endif
+
H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, NULL)
if ( ( entry_ptr->is_dirty ) && ( ! (entry_ptr->in_slist) ) ) {
@@ -4017,11 +4323,11 @@ H5C_protect(H5F_t * f,
*/
H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, NULL)
- /* If the entry's type has a 'notify' callback send a 'after insertion'
+ /* 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_INSERT, entry_ptr) < 0)
+ (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")
}
@@ -4083,7 +4389,6 @@ H5C_protect(H5F_t * f,
have_write_permitted = TRUE;
- first_flush = TRUE;
}
} else {
@@ -4091,7 +4396,6 @@ H5C_protect(H5F_t * f,
have_write_permitted = TRUE;
- first_flush = TRUE;
}
}
@@ -4100,10 +4404,8 @@ H5C_protect(H5F_t * f,
(cache_ptr->resize_ctl).epoch_length ) ) {
result = H5C__auto_adjust_cache_size(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- write_permitted,
- &first_flush);
+ dxpl_id,
+ write_permitted);
if ( result != SUCCEED ) {
HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \
@@ -4137,10 +4439,10 @@ H5C_protect(H5F_t * f,
if(cache_ptr->index_size > cache_ptr->max_cache_size)
cache_ptr->cache_full = TRUE;
- result = H5C_make_space_in_cache(f, primary_dxpl_id,
- secondary_dxpl_id,
- (size_t)0, write_permitted,
- &first_flush);
+ result = H5C_make_space_in_cache(f,
+ dxpl_id,
+ (size_t)0,
+ write_permitted);
if ( result < 0 ) {
@@ -4636,6 +4938,10 @@ done:
* total_entries_skipped_in_msic, total_entries_scanned_in_msic,
* and max_entries_skipped_in_msic fields.
*
+ * JRM -- 4/11/15
+ * Added code displaying the new slist_scan_restarts,
+ * LRU_scan_restarts, and hash_bucket_scan_restarts fields;
+ *
*-------------------------------------------------------------------------
*/
herr_t
@@ -4659,6 +4965,7 @@ H5C_stats(H5C_t * cache_ptr,
int64_t total_clears = 0;
int64_t total_flushes = 0;
int64_t total_evictions = 0;
+ int64_t total_take_ownerships = 0;
int64_t total_moves = 0;
int64_t total_entry_flush_moves = 0;
int64_t total_cache_flush_moves = 0;
@@ -4687,6 +4994,8 @@ H5C_stats(H5C_t * cache_ptr,
FUNC_ENTER_NOAPI(FAIL)
+ HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
+
/* This would normally be an assert, but we need to use an HGOTO_ERROR
* call to shut up the compiler.
*/
@@ -4713,6 +5022,7 @@ H5C_stats(H5C_t * cache_ptr,
total_clears += cache_ptr->clears[i];
total_flushes += cache_ptr->flushes[i];
total_evictions += cache_ptr->evictions[i];
+ total_take_ownerships += cache_ptr->take_ownerships[i];
total_moves += cache_ptr->moves[i];
total_entry_flush_moves += cache_ptr->entry_flush_moves[i];
total_cache_flush_moves += cache_ptr->cache_flush_moves[i];
@@ -4864,11 +5174,16 @@ H5C_stats(H5C_t * cache_ptr,
(long)max_read_protects);
HDfprintf(stdout,
- "%s Total clears / flushes / evictions = %ld / %ld / %ld\n",
+ "%s Total clears / flushes = %ld / %ld\n",
cache_ptr->prefix,
(long)total_clears,
- (long)total_flushes,
- (long)total_evictions);
+ (long)total_flushes);
+
+ HDfprintf(stdout,
+ "%s Total evictions / take ownerships = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)total_evictions,
+ (long)total_take_ownerships);
HDfprintf(stdout,
"%s Total insertions(pinned) / moves = %ld(%ld) / %ld\n",
@@ -4940,6 +5255,13 @@ H5C_stats(H5C_t * cache_ptr,
(long long)(cache_ptr->total_entries_scanned_in_msic -
cache_ptr->entries_scanned_to_make_space));
+ HDfprintf(stdout,
+ "%s slist/LRU/hash bkt scan restarts = %lld / %lld / %lld.\n",
+ cache_ptr->prefix,
+ (long long)(cache_ptr->slist_scan_restarts),
+ (long long)(cache_ptr->LRU_scan_restarts),
+ (long long)(cache_ptr->hash_bucket_scan_restarts));
+
#if H5C_COLLECT_CACHE_ENTRY_STATS
HDfprintf(stdout, "%s aggregate max / min accesses = %d / %d\n",
@@ -4993,11 +5315,16 @@ H5C_stats(H5C_t * cache_ptr,
(int)(cache_ptr->max_read_protects[i]));
HDfprintf(stdout,
- "%s clears / flushes / evictions = %ld / %ld / %ld\n",
+ "%s clears / flushes = %ld / %ld\n",
cache_ptr->prefix,
(long)(cache_ptr->clears[i]),
- (long)(cache_ptr->flushes[i]),
- (long)(cache_ptr->evictions[i]));
+ (long)(cache_ptr->flushes[i]));
+
+ HDfprintf(stdout,
+ "%s evictions / take ownerships = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->evictions[i]),
+ (long)(cache_ptr->take_ownerships[i]));
HDfprintf(stdout,
"%s insertions(pinned) / moves = %ld(%ld) / %ld\n",
@@ -5092,6 +5419,11 @@ done:
* total_entries_skipped_in_msic, total_entries_scanned_in_msic,
* and max_entries_skipped_in_msic fields.
*
+ * JRM 4/11/15
+ * Added code to initialize the new slist_scan_restarts,
+ * LRU_scan_restarts, hash_bucket_scan_restarts, and
+ * take_ownerships fields.
+ *
*-------------------------------------------------------------------------
*/
void
@@ -5125,6 +5457,7 @@ H5C_stats__reset(H5C_t H5_ATTR_UNUSED * cache_ptr)
cache_ptr->clears[i] = 0;
cache_ptr->flushes[i] = 0;
cache_ptr->evictions[i] = 0;
+ cache_ptr->take_ownerships[i] = 0;
cache_ptr->moves[i] = 0;
cache_ptr->entry_flush_moves[i] = 0;
cache_ptr->cache_flush_moves[i] = 0;
@@ -5167,6 +5500,10 @@ H5C_stats__reset(H5C_t H5_ATTR_UNUSED * cache_ptr)
cache_ptr->max_entries_scanned_in_msic = 0;
cache_ptr->entries_scanned_to_make_space = 0;
+ cache_ptr->slist_scan_restarts = 0;
+ cache_ptr->LRU_scan_restarts = 0;
+ cache_ptr->hash_bucket_scan_restarts = 0;
+
#if H5C_COLLECT_CACHE_ENTRY_STATS
for ( i = 0; i <= cache_ptr->max_type_id; i++ )
@@ -5319,6 +5656,107 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5C_dump_cache_skip_list
+ *
+ * Purpose: Debugging routine that prints a summary of the contents of
+ * the skip list used by the metadata cache metadata cache to
+ * maintain an address sorted list of dirty entries.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 11/15/14
+ *
+ *-------------------------------------------------------------------------
+ */
+#if 0 /* debugging routine */
+herr_t
+H5C_dump_cache_skip_list(H5C_t * cache_ptr, char * calling_fcn)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ int i;
+ H5C_cache_entry_t * entry_ptr = NULL;
+ H5SL_node_t * node_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ HDassert(cache_ptr != NULL);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(calling_fcn != NULL);
+
+ HDfprintf(stdout, "\n\nDumping metadata cache skip list from %s.\n",
+ calling_fcn);
+ HDfprintf(stdout, " slist len = %d.\n", cache_ptr->slist_len);
+ HDfprintf(stdout, " slist size = %lld.\n",
+ (long long)(cache_ptr->slist_size));
+
+ if ( cache_ptr->slist_len > 0 )
+ {
+ /* If we get this far, all entries in the cache are listed in the
+ * skip list -- scan the skip list generating the desired output.
+ */
+
+ HDfprintf(stdout,
+ "Num: Addr: Len: Prot/Pind: Dirty: Type:\n");
+
+ i = 0;
+
+ node_ptr = H5SL_first(cache_ptr->slist_ptr);
+
+ if ( node_ptr != NULL ) {
+
+ entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
+
+ } else {
+
+ entry_ptr = NULL;
+ }
+
+ while ( entry_ptr != NULL ) {
+
+ HDassert( entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC );
+
+ HDfprintf(stdout,
+ "%s%d 0x%016llx %4lld %d/%d %d %s\n",
+ cache_ptr->prefix, i,
+ (long long)(entry_ptr->addr),
+ (long long)(entry_ptr->size),
+ (int)(entry_ptr->is_protected),
+ (int)(entry_ptr->is_pinned),
+ (int)(entry_ptr->is_dirty),
+ entry_ptr->type->name);
+
+ HDfprintf(stdout, " node_ptr = 0x%llx, item = 0x%llx\n",
+ (unsigned long long)node_ptr,
+ (unsigned long long)H5SL_item(node_ptr));
+
+ /* increment node_ptr before we delete its target */
+ node_ptr = H5SL_next(node_ptr);
+
+ if ( node_ptr != NULL ) {
+
+ entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
+
+ } else {
+
+ entry_ptr = NULL;
+ }
+
+ i++;
+ }
+ }
+
+ HDfprintf(stdout, "\n\n");
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C_dump_cache_skip_list() */
+#endif /* debugging routine */
+
+
+/*-------------------------------------------------------------------------
* Function: H5C_unpin_entry_from_client()
*
* Purpose: Internal routine to unpin a cache entry from a client action.
@@ -5474,8 +5912,7 @@ done:
*/
herr_t
H5C_unprotect(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
+ hid_t dxpl_id,
const H5C_class_t * type,
haddr_t addr,
void * thing,
@@ -5515,8 +5952,6 @@ H5C_unprotect(H5F_t * f,
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( type );
- HDassert( type->clear );
- HDassert( type->flush );
HDassert( H5F_addr_defined(addr) );
HDassert( thing );
HDassert( ! ( pin_entry && unpin_entry ) );
@@ -5615,6 +6050,19 @@ H5C_unprotect(H5F_t * f,
/* Mark the entry as dirty if appropriate */
entry_ptr->is_dirty = (entry_ptr->is_dirty || dirtied);
+ /* the image_up_to_date field was introduced to support
+ * journaling. Until we re-introduce journaling, this
+ * field should be equal to !entry_ptr->is_dirty.
+ *
+ * When journaling is re-enabled it should be set
+ * to FALSE if dirtied is TRUE.
+ */
+#if 1 /* JRM */
+ entry_ptr->image_up_to_date = FALSE;
+#else /* JRM */
+ entry_ptr->image_up_to_date = !entry_ptr->is_dirty;
+#endif /* JRM */
+
/* Update index for newly dirtied entry */
if(was_clean && entry_ptr->is_dirty)
H5C__UPDATE_INDEX_FOR_ENTRY_DIRTY(cache_ptr, entry_ptr)
@@ -5656,13 +6104,6 @@ H5C_unprotect(H5F_t * f,
* JRM - 5/19/04
*/
if ( deleted ) {
-
- /* the following first flush flag will never be used as we are
- * calling H5C_flush_single_entry with both the
- * H5C__FLUSH_CLEAR_ONLY_FLAG and H5C__FLUSH_INVALIDATE_FLAG flags.
- * However, it is needed for the function call.
- */
- hbool_t dummy_first_flush = TRUE;
unsigned flush_flags = (H5C__FLUSH_CLEAR_ONLY_FLAG |
H5C__FLUSH_INVALIDATE_FLAG);
@@ -5684,36 +6125,40 @@ H5C_unprotect(H5F_t * f,
"hash table contains multiple entries for addr?!?.")
}
- /* Pass along 'free file space' flag to cache client */
-
- entry_ptr->free_file_space_on_destroy = free_file_space;
+ /* 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;
if ( H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- type,
+ dxpl_id,
addr,
flush_flags,
- &dummy_first_flush,
- TRUE) < 0 ) {
+ TRUE,
+ NULL) < 0 ) {
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Can't flush.")
}
+
+#if H5C_DO_SANITY_CHECKS
+ if ( ( take_ownership ) && ( ! was_clean ) )
+ {
+ /* we have just removed an entry from the skip list. Thus
+ * we must touch up cache_ptr->slist_len_increase and
+ * cache_ptr->slist_size_increase to keep from skewing
+ * the sanity checks on flushes.
+ */
+ cache_ptr->slist_len_increase -= 1;
+ cache_ptr->slist_size_increase -= (int64_t)(entry_ptr->size);
+ }
+#endif /* H5C_DO_SANITY_CHECKS */
}
#ifdef H5_HAVE_PARALLEL
else if ( clear_entry ) {
- /* the following first flush flag will never be used as we are
- * calling H5C_flush_single_entry with the
- * H5C__FLUSH_CLEAR_ONLY_FLAG flag. However, it is needed for
- * the function call.
- */
- hbool_t dummy_first_flush = TRUE;
-
/* verify that the target entry is in the cache. */
H5C__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL)
@@ -5730,13 +6175,11 @@ H5C_unprotect(H5F_t * f,
}
if ( H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- type,
+ dxpl_id,
addr,
H5C__FLUSH_CLEAR_ONLY_FLAG,
- &dummy_first_flush,
- TRUE) < 0 ) {
+ TRUE,
+ NULL) < 0 ) {
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Can't clear.")
}
@@ -6392,10 +6835,8 @@ done:
*/
static herr_t
H5C__auto_adjust_cache_size(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- hbool_t write_permitted,
- hbool_t * first_flush_ptr)
+ hid_t dxpl_id,
+ hbool_t write_permitted)
{
H5C_t * cache_ptr = f->shared->cache;
herr_t result;
@@ -6596,13 +7037,11 @@ H5C__auto_adjust_cache_size(H5F_t * f,
} else {
result = H5C__autoadjust__ageout(f,
+ dxpl_id,
hit_rate,
&status,
&new_max_cache_size,
- primary_dxpl_id,
- secondary_dxpl_id,
- write_permitted,
- first_flush_ptr);
+ write_permitted);
if ( result != SUCCEED ) {
@@ -6745,13 +7184,11 @@ done:
*/
static herr_t
H5C__autoadjust__ageout(H5F_t * f,
+ hid_t dxpl_id,
double hit_rate,
enum H5C_resize_status * status_ptr,
size_t * new_max_cache_size_ptr,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- hbool_t write_permitted,
- hbool_t * first_flush_ptr)
+ hbool_t write_permitted)
{
H5C_t * cache_ptr = f->shared->cache;
herr_t result;
@@ -6792,8 +7229,7 @@ H5C__autoadjust__ageout(H5F_t * f,
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, primary_dxpl_id,
- secondary_dxpl_id, write_permitted, first_flush_ptr) < 0)
+ if(H5C__autoadjust__ageout__evict_aged_out_entries(f, dxpl_id, write_permitted) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "error flushing aged out entries.")
/* ... and then reduce cache size if appropriate */
@@ -6990,20 +7426,32 @@ done:
*
* Programmer: John Mainzer, 11/22/04
*
+ * Changes: Modified function to detect deletions of entries
+ * during a scan of the LRU, and where appropriate,
+ * restart the scan to avoid proceeding with a next
+ * entry that is no longer in the cache.
+ *
+ * Note the absence of checks after flushes of clean
+ * entries. As a second entry can only be removed by
+ * by a call to the pre_serialize or serialize callback
+ * of the first, and as these callbacks will not be called
+ * on clean entries, no checks are needed.
+ *
+ * JRM -- 4/6/15
+ *
*-------------------------------------------------------------------------
*/
static herr_t
H5C__autoadjust__ageout__evict_aged_out_entries(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- hbool_t write_permitted,
- hbool_t * first_flush_ptr)
+ hid_t dxpl_id,
+ hbool_t write_permitted)
{
H5C_t * cache_ptr = f->shared->cache;
herr_t result;
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;
@@ -7032,13 +7480,17 @@ H5C__autoadjust__ageout__evict_aged_out_entries(H5F_t * f,
if ( write_permitted ) {
+ restart_scan = FALSE;
entry_ptr = cache_ptr->LRU_tail_ptr;
while ( ( entry_ptr != NULL ) &&
( (entry_ptr->type)->id != H5C__EPOCH_MARKER_TYPE ) &&
( bytes_evicted < eviction_size_limit ) )
{
+ 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;
@@ -7050,26 +7502,39 @@ H5C__autoadjust__ageout__evict_aged_out_entries(H5F_t * f,
if ( entry_ptr->is_dirty ) {
+ /* 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;
+
result = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- entry_ptr->type,
+ dxpl_id,
entry_ptr->addr,
H5C__NO_FLAGS_SET,
- first_flush_ptr,
- FALSE);
+ FALSE,
+ NULL);
+
+ if ( ( cache_ptr->entries_removed_counter > 1 ) ||
+ ( cache_ptr->last_entry_removed_ptr == prev_ptr ) )
+
+ restart_scan = TRUE;
+
} else {
bytes_evicted += entry_ptr->size;
result = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- entry_ptr->type,
+ dxpl_id,
entry_ptr->addr,
H5C__FLUSH_INVALIDATE_FLAG,
- first_flush_ptr,
- TRUE);
+ TRUE,
+ NULL);
}
if ( result < 0 ) {
@@ -7079,30 +7544,25 @@ H5C__autoadjust__ageout__evict_aged_out_entries(H5F_t * f,
}
if ( prev_ptr != NULL ) {
-#ifndef NDEBUG
- if ( prev_ptr->magic != H5C__H5C_CACHE_ENTRY_T_MAGIC ) {
-
- /* something horrible has happened to *prev_ptr --
- * scream and die.
- */
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "*prev_ptr corrupt")
- } else
-#endif /* NDEBUG */
- if ( ( prev_ptr->is_dirty != prev_is_dirty )
- ||
- ( prev_ptr->next != next_ptr )
- ||
- ( prev_ptr->is_protected )
- ||
- ( prev_ptr->is_pinned ) ) {
+ 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;
@@ -7161,13 +7621,11 @@ H5C__autoadjust__ageout__evict_aged_out_entries(H5F_t * f,
if ( ! (entry_ptr->is_dirty) ) {
result = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- entry_ptr->type,
+ dxpl_id,
entry_ptr->addr,
H5C__FLUSH_INVALIDATE_FLAG,
- first_flush_ptr,
- TRUE);
+ TRUE,
+ NULL);
if ( result < 0 ) {
@@ -7177,6 +7635,10 @@ H5C__autoadjust__ageout__evict_aged_out_entries(H5F_t * f,
}
/* 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;
@@ -7640,17 +8102,45 @@ done:
* Programmer: John Mainzer
* 3/24/065
*
+ * 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 in H5C_flush_cache.
+ *
+ * At present, this issue doesn't seem to be causing problems
+ * in H5C_flush_invalidate_cache(). However, it seems
+ * prudent to port the H5C_flush_cache changes to this
+ * function as well.
+ *
+ * JRM -- 12/14/14
+ *
+ * Added code to track entry size change during flush single
+ * entry. This didn't used to be a problem, as the entry
+ * was largely removed from the cache data structures before
+ * the flush proper. However, re-entrant calls to the cache
+ * in the parallel case required a re-factoring of the
+ * H5C_flush_single_entry() function to keep entries fully
+ * in the cache until after the pre-serialize and serialize
+ * calls.
+ * JRM -- 12/25/14
+ *
*-------------------------------------------------------------------------
*/
static herr_t
-H5C_flush_invalidate_cache(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
+H5C_flush_invalidate_cache(const H5F_t * f,
+ hid_t dxpl_id,
unsigned flags)
{
H5C_t * cache_ptr = f->shared->cache;
herr_t status;
- hbool_t first_flush = TRUE;
+ hbool_t restart_slist_scan;
int32_t protected_entries = 0;
int32_t i;
int32_t cur_pel_len;
@@ -7661,10 +8151,14 @@ H5C_flush_invalidate_cache(H5F_t * f,
H5C_cache_entry_t * entry_ptr = NULL;
H5C_cache_entry_t * next_entry_ptr = NULL;
#if H5C_DO_SANITY_CHECKS
- int64_t actual_slist_len = 0;
+ int64_t flushed_slist_len = 0;
int64_t initial_slist_len = 0;
- size_t actual_slist_size = 0;
+ int64_t flushed_slist_size = 0;
size_t initial_slist_size = 0;
+ int64_t entry_size_change;
+ int64_t * entry_size_change_ptr = &entry_size_change;
+#else /* H5C_DO_SANITY_CHECKS */
+ int64_t * entry_size_change_ptr = NULL;
#endif /* H5C_DO_SANITY_CHECKS */
herr_t ret_value = SUCCEED;
@@ -7743,25 +8237,6 @@ H5C_flush_invalidate_cache(H5F_t * f,
* 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 ( cache_ptr->slist_len == 0 ) {
-
- node_ptr = NULL;
- HDassert( cache_ptr->slist_size == 0 );
-
- } else {
-
- /* Start at beginning of skip list each time */
- node_ptr = H5SL_first(cache_ptr->slist_ptr);
- HDassert( node_ptr != NULL );
-
- /* 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 );
- }
#if H5C_DO_SANITY_CHECKS
/* Depending on circumstances, H5C_flush_single_entry() will
@@ -7785,63 +8260,80 @@ H5C_flush_invalidate_cache(H5F_t * f,
cache_ptr->slist_len_increase = 0;
cache_ptr->slist_size_increase = 0;
- /* Finally, reset the actual_slist_len and actual_slist_size
+ /* Finally, reset the flushed_slist_len and flushed_slist_size
* fields to zero, as these fields are used to accumulate
* the slist lenght and size that we see as we scan through
* the slist.
*/
- actual_slist_len = 0;
- actual_slist_size = 0;
+ flushed_slist_len = 0;
+ flushed_slist_size = 0;
#endif /* H5C_DO_SANITY_CHECKS */
- while ( node_ptr != NULL )
+ /* set the cache_ptr->slist_change_in_pre_serialize and
+ * cache_ptr->slist_change_in_serialize to false.
+ *
+ * These flags are set to TRUE by H5C_flush_single_entry if the
+ * slist is modified by a pre_serialize or serialize call
+ * respectively.
+ *
+ * H5C_flush_invalidate_cache() uses these flags 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_change_in_pre_serialize = FALSE;
+ cache_ptr->slist_change_in_serialize = FALSE;
+
+ /* this done, start the scan of the slist */
+
+ restart_slist_scan = TRUE;
+
+ while ( ( restart_slist_scan ) || ( node_ptr != NULL ) )
{
- entry_ptr = next_entry_ptr;
+ if ( restart_slist_scan )
+ {
+ restart_slist_scan = FALSE;
- /* With the advent of the fractal heap, it is possible
- * that the flush callback will dirty and/or resize
- * other entries in the cache. In particular, while
- * Quincey has promised me that this will never happen,
- * it is possible that the flush callback for an
- * entry may protect an entry that is not in the cache,
- * perhaps causing the cache to flush and possibly
- * evict the entry associated with node_ptr to make
- * space for the new entry.
- *
- * Thus we do a bit of extra sanity checking on entry_ptr,
- * and break out of this scan of the skip list if we
- * detect major problems. We have a bit of leaway on the
- * number of passes though the skip list, so this shouldn't
- * be an issue in the flush in and of itself, as it should
- * be all but impossible for this to happen more than once
- * in any flush.
- *
- * Observe that that breaking out of the scan early
- * shouldn't break the sanity checks just after the end
- * of this while loop.
- *
- * If an entry has merely been marked clean and removed from
- * the s-list, we simply break out of the scan.
- *
- * If the entry has been evicted, we flag an error and
- * exit.
- */
-#ifndef NDEBUG
- if ( entry_ptr->magic != H5C__H5C_CACHE_ENTRY_T_MAGIC ) {
+ /* Start at beginning of skip list */
+ node_ptr = H5SL_first(cache_ptr->slist_ptr);
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "entry_ptr->magic is invalid ?!?!")
+ if ( node_ptr == NULL )
+ {
+ /* the slist is empty -- break out of inner loop */
+ break;
+ }
+ HDassert( node_ptr != NULL );
- } else
-#endif /* NDEBUG */
- if ( ( ! entry_ptr->is_dirty ) ||
- ( ! entry_ptr->in_slist ) ) {
+ /* Get cache entry for this node */
+ next_entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
- /* the s-list has been modified out from under us.
- * break out of the loop.
- */
- goto end_of_inner_loop;;
+ 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 );
}
+ 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->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC );
+ HDassert( entry_ptr->in_slist );
+ HDassert( entry_ptr->is_dirty );
+
/* increment node pointer now, before we delete its target
* from the slist.
*/
@@ -7854,6 +8346,7 @@ H5C_flush_invalidate_cache(H5F_t * f,
HDassert( next_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC );
HDassert( next_entry_ptr->is_dirty );
HDassert( next_entry_ptr->in_slist );
+ HDassert( entry_ptr != next_entry_ptr );
} else {
next_entry_ptr = NULL;
}
@@ -7877,21 +8370,6 @@ H5C_flush_invalidate_cache(H5F_t * f,
( cache_ptr->num_last_entries >=
cache_ptr->slist_len ) ) ) {
-#if H5C_DO_SANITY_CHECKS
- /* update actual_slist_len & actual_slist_size before
- * the flush. Note that the entry will be removed
- * from the slist after the flush, and thus may be
- * resized by the flush callback. This is OK, as
- * we will catch the size delta in
- * cache_ptr->slist_size_increase.
- *
- * Note that we include pinned entries in this count, even
- * though we will not actually flush them.
- */
- actual_slist_len++;
- actual_slist_size += entry_ptr->size;
-#endif /* H5C_DO_SANITY_CHECKS */
-
if ( entry_ptr->is_protected ) {
/* we have major problems -- but lets flush
@@ -7907,14 +8385,26 @@ H5C_flush_invalidate_cache(H5F_t * f,
* as pinned entries can't be evicted.
*/
if(entry_ptr->flush_dep_height == curr_flush_dep_height ) {
+#if H5C_DO_SANITY_CHECKS
+ /* update flushed_slist_len & flushed_slist_size
+ * before the flush. Note that the entry will
+ * be removed from the slist after the flush,
+ * and thus may be resized by the flush callback.
+ * This is OK, as we will catch the size delta in
+ * cache_ptr->slist_size_increase.
+ *
+ */
+ flushed_slist_len++;
+ flushed_slist_size += (int64_t)entry_ptr->size;
+ entry_size_change = 0;
+#endif /* H5C_DO_SANITY_CHECKS */
+
status = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- NULL,
+ dxpl_id,
entry_ptr->addr,
H5C__NO_FLAGS_SET,
- &first_flush,
- FALSE);
+ FALSE,
+ entry_size_change_ptr);
if ( status < 0 ) {
/* This shouldn't happen -- if it does, we
@@ -7924,7 +8414,32 @@ H5C_flush_invalidate_cache(H5F_t * f,
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
"dirty pinned entry flush failed.")
} /* end if */
+
+#if H5C_DO_SANITY_CHECKS
+ /* entry size may have changed during the flush.
+ * Update flushed_slist_size to account for this.
+ */
+ flushed_slist_size += entry_size_change;
+#endif /* H5C_DO_SANITY_CHECKS */
+
flushed_during_dep_loop = TRUE;
+
+ if ( ( cache_ptr->slist_change_in_serialize ) ||
+ ( cache_ptr->slist_change_in_pre_serialize ) )
+ {
+ /* 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_change_in_pre_serialize
+ = FALSE;
+ cache_ptr->slist_change_in_serialize = FALSE;
+ H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr);
+ }
} /* end if */
else if(entry_ptr->flush_dep_height < curr_flush_dep_height)
/* This shouldn't happen -- if it does, just scream and die. */
@@ -7932,15 +8447,26 @@ H5C_flush_invalidate_cache(H5F_t * f,
} /* end if */
else {
if(entry_ptr->flush_dep_height == curr_flush_dep_height ){
+#if H5C_DO_SANITY_CHECKS
+ /* update flushed_slist_len & flushed_slist_size
+ * before the flush. Note that the entry will
+ * be removed from the slist after the flush,
+ * and thus may be resized by the flush callback.
+ * This is OK, as we will catch the size delta in
+ * cache_ptr->slist_size_increase.
+ *
+ */
+ flushed_slist_len++;
+ flushed_slist_size += (int64_t)entry_ptr->size;
+ entry_size_change = 0;
+#endif /* H5C_DO_SANITY_CHECKS */
status = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- NULL,
- entry_ptr->addr,
- (cooked_flags | H5C__FLUSH_INVALIDATE_FLAG),
- &first_flush,
- TRUE);
+ dxpl_id,
+ entry_ptr->addr,
+ (cooked_flags | H5C__FLUSH_INVALIDATE_FLAG),
+ TRUE,
+ entry_size_change_ptr);
if ( status < 0 ) {
/* This shouldn't happen -- if it does, we
@@ -7950,7 +8476,32 @@ H5C_flush_invalidate_cache(H5F_t * f,
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
"dirty entry flush destroy failed.")
} /* end if */
+
+#if H5C_DO_SANITY_CHECKS
+ /* entry size may have changed during the flush.
+ * Update flushed_slist_size to account for this.
+ */
+ flushed_slist_size += entry_size_change;
+#endif /* H5C_DO_SANITY_CHECKS */
+
flushed_during_dep_loop = TRUE;
+
+ if ((cache_ptr->slist_change_in_serialize) ||
+ (cache_ptr->slist_change_in_pre_serialize))
+ {
+ /* 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_change_in_pre_serialize
+ = FALSE;
+ cache_ptr->slist_change_in_serialize = FALSE;
+ H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr)
+ }
} /* end if */
else if(entry_ptr->flush_dep_height < curr_flush_dep_height)
/* This shouldn't happen -- if it does, just scream and die. */
@@ -7971,11 +8522,10 @@ H5C_flush_invalidate_cache(H5F_t * f,
if ( node_ptr == NULL ) {
- HDassert( (actual_slist_len + cache_ptr->slist_len) ==
+ HDassert( (flushed_slist_len + cache_ptr->slist_len) ==
(initial_slist_len + cache_ptr->slist_len_increase) );
- HDassert( (actual_slist_size + cache_ptr->slist_size) ==
- (initial_slist_size +
- (size_t)(cache_ptr->slist_size_increase)) );
+ HDassert( (flushed_slist_size + (int64_t)cache_ptr->slist_size) ==
+ ((int64_t)initial_slist_size + cache_ptr->slist_size_increase) );
}
#endif /* H5C_DO_SANITY_CHECKS */
@@ -7997,9 +8547,7 @@ H5C_flush_invalidate_cache(H5F_t * f,
while ( next_entry_ptr != NULL )
{
entry_ptr = next_entry_ptr;
-#ifndef NDEBUG
HDassert( entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC );
-#endif /* NDEBUG */
next_entry_ptr = entry_ptr->ht_next;
HDassert ( ( next_entry_ptr == NULL ) ||
@@ -8028,14 +8576,40 @@ H5C_flush_invalidate_cache(H5F_t * f,
* If we can, go ahead and flush.
*/
if(entry_ptr->flush_dep_height == curr_flush_dep_height ){
+ /* 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.
+ *
+ * If this happens, and one of the target
+ * entries happens to be the next entry in
+ * the hash bucket, we could find ourselves
+ * 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 *entry_ptr was dirty,
+ * on the off chance that the next entry was
+ * a target.
+ *
+ * This is not as inefficient at it might seem,
+ * as hash buckets typically have at most two
+ * or three entries.
+ */
+ hbool_t entry_was_dirty;
+
+ entry_was_dirty = entry_ptr->is_dirty;
+
status = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- NULL,
- entry_ptr->addr,
- (cooked_flags | H5C__FLUSH_INVALIDATE_FLAG),
- &first_flush,
- TRUE);
+ dxpl_id,
+ entry_ptr->addr,
+ (cooked_flags | H5C__FLUSH_INVALIDATE_FLAG),
+ TRUE,
+ NULL);
+
if ( status < 0 ) {
/* This shouldn't happen -- if it does,
@@ -8045,6 +8619,17 @@ H5C_flush_invalidate_cache(H5F_t * f,
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
"Entry flush destroy failed.")
}
+
+ if ( entry_was_dirty ) {
+
+ /* update stats for hash bucket scan
+ * restart here.
+ * -- JRM
+ */
+ next_entry_ptr = cache_ptr->index[i];
+ H5C__UPDATE_STATS_FOR_HASH_BUCKET_SCAN_RESTART(cache_ptr)
+ }
+
flushed_during_dep_loop = TRUE;
} /* end if */
else if(entry_ptr->flush_dep_height < curr_flush_dep_height)
@@ -8060,7 +8645,7 @@ H5C_flush_invalidate_cache(H5F_t * f,
* of pinned entries from pass to pass. If it stops
* shrinking before it hits zero, we scream and die.
*/
- /* if the flush function on the entry we last evicted
+ /* if the serialize function on the entry we last evicted
* loaded an entry into cache (as Quincey has promised me
* it never will), and if the cache was full, it is
* possible that *next_entry_ptr was flushed or evicted.
@@ -8069,8 +8654,13 @@ H5C_flush_invalidate_cache(H5F_t * f,
* test is triggred, we are accessing a deallocated piece
* of dynamically allocated memory, so we just scream and
* die.
+ *
+ * Update: The code to restart the scan after flushes
+ * of dirty entries should make it impossible
+ * to satisfy the following test. Leave it in
+ * in case I am wrong.
+ * -- JRM
*/
-#ifndef NDEBUG
if ( ( next_entry_ptr != NULL ) &&
( next_entry_ptr->magic !=
H5C__H5C_CACHE_ENTRY_T_MAGIC ) ) {
@@ -8081,7 +8671,6 @@ H5C_flush_invalidate_cache(H5F_t * f,
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
"next_entry_ptr->magic is invalid?!?!?.")
}
-#endif /* NDEBUG */
} /* end while loop scanning hash table bin */
} /* end for loop scanning hash table */
@@ -8099,7 +8688,6 @@ H5C_flush_invalidate_cache(H5F_t * f,
curr_flush_dep_height++;
} /* end while loop over flush dependency heights */
-end_of_inner_loop:
old_pel_len = cur_pel_len;
cur_pel_len = cache_ptr->pel_len;
@@ -8177,22 +8765,9 @@ done:
* Attempts to flush a protected entry will result in an
* error.
*
- * *first_flush_ptr should be true if only one
- * flush is contemplated before the next load, or if this
- * is the first of a sequence of flushes that will be
- * completed before the next load. *first_flush_ptr is set
- * to false if a flush actually takes place, and should be
- * left false until the end of the sequence.
- *
- * The primary_dxpl_id is used if *first_flush_ptr is TRUE
- * on entry, and a flush actually takes place. The
- * secondary_dxpl_id is used in any subsequent flush where
- * *first_flush_ptr is FALSE on entry.
- *
* If the H5C__FLUSH_INVALIDATE_FLAG flag is set, the entry will
- * be cleared and not flushed -- in the case *first_flush_ptr,
- * primary_dxpl_id, and secondary_dxpl_id are all irrelevent,
- * and the call can't be part of a sequence of flushes.
+ * 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
@@ -8209,26 +8784,57 @@ done:
*
* Programmer: John Mainzer, 5/5/04
*
+ * Changes: Refactored function to remove the type_ptr parameter.
+ *
+ * JRM -- 8/7/14
+ *
+ * Added code to check for slist changes in pre_serialize and
+ * serialize calls, and set
+ * cache_ptr->slist_change_in_pre_serialize and
+ * cache_ptr->slist_change_in_serialize as appropriate.
+ *
+ * JRM -- 12/13/14
+ *
+ * Refactored function to delay all modifications of the
+ * metadata cache data structures until after any calls
+ * to the pre-serialize or serialize callbacks.
+ *
+ * Need to do this, as some pre-serialize or serialize
+ * calls result in calls to the metadata cache and
+ * modifications to its data structures. Thus, at the
+ * time of any such call, the target entry flags and
+ * the metadata cache must all be consistant.
+ *
+ * Also added the entry_size_change_ptr parameter, which
+ * allows the function to report back any change in the size
+ * of the entry during the flush. Such size changes may
+ * occur during the pre-serialize callback.
+ *
+ * JRM -- 12/24/14
+ *
*-------------------------------------------------------------------------
*/
static herr_t
-H5C_flush_single_entry(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
- const H5C_class_t * type_ptr,
+H5C_flush_single_entry(const H5F_t * f,
+ hid_t dxpl_id,
haddr_t addr,
unsigned flags,
- hbool_t * first_flush_ptr,
- hbool_t del_entry_from_slist_on_destroy)
+ hbool_t del_entry_from_slist_on_destroy,
+ int64_t * entry_size_change_ptr)
{
H5C_t * cache_ptr = f->shared->cache;
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 write_entry; /* internal flag */
+ hbool_t destroy_entry; /* internal flag */
hbool_t was_dirty;
- hbool_t destroy_entry;
herr_t status;
- unsigned flush_flags = H5C_CALLBACK__NO_FLAGS_SET;
+ haddr_t new_addr = HADDR_UNDEF;
+ haddr_t old_addr = HADDR_UNDEF;
+ size_t new_len = 0;
+ size_t new_compressed_len = 0;
H5C_cache_entry_t * entry_ptr = NULL;
herr_t ret_value = SUCCEED; /* Return value */
@@ -8238,11 +8844,15 @@ H5C_flush_single_entry(H5F_t * f,
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( H5F_addr_defined(addr) );
- HDassert( first_flush_ptr );
+
+ /* If defined, initialize *entry_size_change_ptr to 0 */
+ if(entry_size_change_ptr != NULL)
+ *entry_size_change_ptr = 0;
/* 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);
/* Set the flag for destroying the entry, based on the 'take ownership'
@@ -8256,6 +8866,14 @@ H5C_flush_single_entry(H5F_t * f,
/* attempt to find the target entry in the hash table */
H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL)
+ /* we will write the entry to disk if it exists, is dirty, and if the
+ * clear only flag is not set.
+ */
+ if ( ( entry_ptr != NULL ) && ( entry_ptr->is_dirty ) && ( ! clear_only ) )
+ write_entry = TRUE;
+ else
+ write_entry = FALSE;
+
/* run initial sanity checks */
#if H5C_DO_SANITY_CHECKS
if ( entry_ptr != NULL ) {
@@ -8264,6 +8882,8 @@ H5C_flush_single_entry(H5F_t * f,
if ( entry_ptr->in_slist ) {
+ HDassert(entry_ptr->is_dirty);
+
if ( ( ( entry_ptr->flush_marker ) && ( ! entry_ptr->is_dirty ) ) ||
( entry_ptr->addr != addr ) ) {
@@ -8272,6 +8892,10 @@ H5C_flush_single_entry(H5F_t * f,
}
} else {
+ HDassert(!entry_ptr->is_dirty);
+ HDassert(!entry_ptr->flush_marker);
+ HDassert(entry_ptr->addr == addr);
+
if ( ( entry_ptr->is_dirty ) ||
( entry_ptr->flush_marker ) ||
( entry_ptr->addr != addr ) ) {
@@ -8285,22 +8909,32 @@ H5C_flush_single_entry(H5F_t * f,
if ( ( entry_ptr != NULL ) && ( 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.")
}
- if ( ( entry_ptr != NULL ) &&
- ( ( type_ptr == NULL ) || ( type_ptr->id == entry_ptr->type->id ) ) )
+ /* if the entry exists, set entry_ptr->flush_in_progress = TRUE
+ * and set entry_ptr->flush_marker = FALSE
+ *
+ * in the parallel case, do some sanity checking in passing.
+ */
+ if ( entry_ptr != NULL )
{
/* we have work to do */
+ HDassert(entry_ptr->type);
+
+ was_dirty = entry_ptr->is_dirty; /* needed later for logging */
/* 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;
+
#ifdef H5_HAVE_PARALLEL
#ifndef NDEBUG
/* If MPI based VFD is used, do special parallel I/O sanity checks.
@@ -8314,7 +8948,7 @@ H5C_flush_single_entry(H5F_t * f,
unsigned coll_meta; /* Collective metadata write flag */
/* Get the dataset transfer property list */
- if(NULL == (dxpl = H5I_object(primary_dxpl_id)))
+ if(NULL == (dxpl = H5I_object(dxpl_id)))
HGOTO_ERROR(H5E_CACHE, H5E_BADTYPE, FAIL, "not a dataset transfer property list")
/* Get the collective metadata write property */
@@ -8326,273 +8960,644 @@ H5C_flush_single_entry(H5F_t * f,
} /* end if */
#endif /* NDEBUG */
#endif /* H5_HAVE_PARALLEL */
+ }
- was_dirty = entry_ptr->is_dirty;
-
- entry_ptr->flush_marker = FALSE;
-
- if ( clear_only ) {
- H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr)
- } else {
- H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr)
- }
-
- if ( destroy ) {
- H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr)
- }
+ if ( ( entry_ptr != NULL ) && ( write_entry ) )
+ {
+ /* serialize the entry if necessary, and then write it to disk. */
- /* 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(destroy) {
- 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")
- } /* end if */
+ unsigned serialize_flags = H5C__SERIALIZE_NO_FLAGS_SET;
- /* Always remove the entry from the hash table on a destroy. On a
- * flush with destroy, it is cheaper to discard the skip list all at
- * once rather than remove the entries one by one, so we only delete
- * from the slist only if requested.
+ /* The entry is dirty, and we are doing either a flush,
+ * or a flush destroy. In either case, serialize the
+ * entry and write it to disk.
*
- * We must do deletions now as the callback routines will free the
- * entry if destroy is true.
+ * Note that this may cause the entry to be re-sized and/or
+ * moved in the cache.
*
- * Note that it is possible that the entry will be moved during
- * its call to flush. This will upset H5C_move_entry() if we
- * don't tell it that it doesn't have to worry about updating the
- * index and SLIST. Use the destroy_in_progress field for this
- * purpose.
+ * 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).
*/
- if ( destroy ) {
- entry_ptr->destroy_in_progress = TRUE;
+ HDassert(entry_ptr->is_dirty);
- H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr)
+#if H5C_DO_SANITY_CHECKS
+ if ( ( cache_ptr->check_write_permitted == NULL ) &&
+ ( !(cache_ptr->write_permitted) ) )
- if ( ( entry_ptr->in_slist ) &&
- ( del_entry_from_slist_on_destroy ) ) {
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Write when writes are always forbidden!?!?!")
+#endif /* H5C_DO_SANITY_CHECKS */
- H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr)
+ if ( NULL == entry_ptr->image_ptr )
+ {
+ size_t image_size;
+
+ if ( entry_ptr->compressed )
+ image_size = entry_ptr->compressed_size;
+ else
+ image_size = entry_ptr->size;
+
+ HDassert(image_size > 0);
+
+ entry_ptr->image_ptr =
+ H5MM_malloc(image_size + H5C_IMAGE_EXTRA_SPACE);
+
+ if ( NULL == entry_ptr->image_ptr)
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, \
+ "memory allocation failed for on disk image buffer")
}
- }
+#if H5C_DO_MEMORY_SANITY_CHECKS
- /* Update the replacement policy for the flush or eviction.
- * Again, do this now so we don't have to reference freed
- * memory in the destroy case.
- */
- if ( destroy ) { /* AKA eviction */
+ HDmemcpy(((uint8_t *)entry_ptr->image_ptr) + image_size,
+ H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
- H5C__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, FAIL)
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
- } else {
+ } /* end if */
- H5C__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, FAIL)
- }
+ if ( ! (entry_ptr->image_up_to_date) )
+ {
+ /* reset cache_ptr->slist_changed so we can detect slist
+ * modifications in the pre_serialize call.
+ */
+ cache_ptr->slist_changed = FALSE;
+
+ /* make note of the entry's current address */
+ old_addr = entry_ptr->addr;
+
+ /* Call client's pre-serialize callback, if there's one */
+ if ( ( entry_ptr->type->pre_serialize != NULL ) &&
+ ( (entry_ptr->type->pre_serialize)(f, dxpl_id,
+ (void *)entry_ptr,
+ entry_ptr->addr,
+ entry_ptr->size,
+ entry_ptr->compressed_size,
+ &new_addr, &new_len,
+ &new_compressed_len,
+ &serialize_flags) < 0 ) )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unable to pre-serialize entry")
+ }
- /* Clear the dirty flag only, if requested */
- if ( clear_only ) {
+ /* set cache_ptr->slist_change_in_pre_serialize if the
+ * slist was modified.
+ */
+ if ( cache_ptr->slist_changed )
+ cache_ptr->slist_change_in_pre_serialize = TRUE;
- if ( destroy ) {
-#ifndef NDEBUG
- /* we are about to call the clear callback with the
- * destroy flag set -- this will result in *entry_ptr
- * being freed. Set the magic field to bad magic
- * so we can detect a freed cache entry if we see
- * one.
+ /* 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 |
+ H5C__SERIALIZE_COMPRESSED_FLAG))
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unknown serialize flag(s)")
+ }
+#ifdef H5_HAVE_PARALLEL
+ /* In the parallel case, resizes and moves in
+ * the serialize operation can cause problems.
+ * If they occur, scream and die.
+ *
+ * At present, in the parallel case, the aux_ptr
+ * will only be set if there is more than one
+ * process. Thus we can use this to detect
+ * the parallel case.
+ *
+ * This works for now, but if we start using the
+ * aux_ptr for other purposes, we will have to
+ * change this test accordingly.
+ *
+ * NB: While this test detects entryies that attempt
+ * to resize or move themselves during a flush
+ * in the parallel case, it will not detect an
+ * entry that dirties, resizes, and/or moves
+ * other entries during its flush.
+ *
+ * From what Quincey tells me, this test is
+ * sufficient for now, as any flush routine that
+ * does the latter will also do the former.
+ *
+ * If that ceases to be the case, further
+ * tests will be necessary.
+ */
+ if ( cache_ptr->aux_ptr != NULL )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "resize/move in serialize occured in parallel case.")
+#endif /* H5_HAVE_PARALLEL */
+
+ /* Resize the buffer if required */
+ if ( ( ( ! entry_ptr->compressed ) &&
+ ( serialize_flags & H5C__SERIALIZE_RESIZED_FLAG ) ) ||
+ ( ( entry_ptr->compressed ) &&
+ ( serialize_flags & H5C__SERIALIZE_COMPRESSED_FLAG ) ) )
+ {
+ size_t new_image_size;
+
+ if ( entry_ptr->compressed )
+ new_image_size = new_compressed_len;
+ else
+ new_image_size = new_len;
+
+ HDassert(new_image_size > 0);
+
+ /* Release the current image */
+ if ( entry_ptr->image_ptr )
+ {
+ entry_ptr->image_ptr = H5MM_xfree(entry_ptr->image_ptr);
+ }
+
+ /* Allocate a new image buffer */
+ entry_ptr->image_ptr =
+ H5MM_malloc(new_image_size + H5C_IMAGE_EXTRA_SPACE);
+
+ if ( NULL == entry_ptr->image_ptr )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, \
+ "memory allocation failed for on disk image buffer")
+ }
+
+#if H5C_DO_MEMORY_SANITY_CHECKS
+
+ HDmemcpy(((uint8_t *)entry_ptr->image_ptr) + new_image_size,
+ H5C_IMAGE_SANITY_VALUE,
+ H5C_IMAGE_EXTRA_SPACE);
+
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+
+ } /* end if */
+
+ /* If required, update the entry and the cache data structures
+ * for a resize.
*/
- entry_ptr->magic = H5C__H5C_CACHE_ENTRY_T_BAD_MAGIC;
-#endif /* NDEBUG */
- entry_ptr->cache_ptr = NULL;
- }
- /* Call the callback routine to clear all dirty flags for object */
- if ( (entry_ptr->type->clear)(f, entry_ptr, destroy_entry) < 0 ) {
+ if ( serialize_flags & H5C__SERIALIZE_RESIZED_FLAG ) {
+
+ 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);
- HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "can't clear entry")
- }
- } else {
+ /* 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->in_slist);
+ H5C__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, \
+ new_len)
+
+ /* if defined, update *entry_size_change_ptr for the
+ * change in entry size.
+ */
+ if(entry_size_change_ptr != NULL)
+ *entry_size_change_ptr = (int64_t)new_len - (int64_t)(entry_ptr->size);
+
+ /* 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)
+ {
#if H5C_DO_SANITY_CHECKS
- if ( ( entry_ptr->is_dirty ) &&
- ( cache_ptr->check_write_permitted == NULL ) &&
- ( ! (cache_ptr->write_permitted) ) )
+ int64_t saved_slist_len_increase;
+ int64_t saved_slist_size_increase;
+#endif /* H5C_DO_SANITY_CHECKS */
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "Write when writes are always forbidden!?!?!")
+ H5C__UPDATE_STATS_FOR_MOVE(cache_ptr, entry_ptr)
+
+ if ( entry_ptr->addr == old_addr )
+ {
+ /* we must update cache data structures for the
+ * change in address.
+ */
+
+ /* delete the entry from the hash table and the slist */
+ H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr)
+ H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr)
+
+ /* 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)
+
+#if H5C_DO_SANITY_CHECKS
+ /* save cache_ptr->slist_len_increase and
+ * cache_ptr->slist_size_increase before the
+ * reinsertion into the slist, and restore
+ * them afterwards to avoid skewing our sanity
+ * checking.
+ */
+ saved_slist_len_increase = cache_ptr->slist_len_increase;
+ saved_slist_size_increase = cache_ptr->slist_size_increase;
#endif /* H5C_DO_SANITY_CHECKS */
- if ( destroy ) {
-#ifndef NDEBUG
- /* we are about to call the flush callback with the
- * destroy flag set -- this will result in *entry_ptr
- * being freed. Set the magic field to bad magic
- * so we can detect a freed cache entry if we see
- * one.
- */
- entry_ptr->magic = H5C__H5C_CACHE_ENTRY_T_BAD_MAGIC;
-#endif /* NDEBUG */
- entry_ptr->cache_ptr = NULL;
- }
+ H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
- /* Only block for all the processes on the first piece of metadata
- */
+#if H5C_DO_SANITY_CHECKS
+ cache_ptr->slist_len_increase = saved_slist_len_increase;
+ cache_ptr->slist_size_increase = saved_slist_size_increase;
+#endif /* H5C_DO_SANITY_CHECKS */
+ }
+ else /* move is alread done for us -- just do sanity checks */
+ {
+ HDassert(entry_ptr->addr == new_addr);
+ }
+ } /* end if */
- if ( *first_flush_ptr && entry_ptr->is_dirty ) {
+ if ( serialize_flags & H5C__SERIALIZE_COMPRESSED_FLAG ) {
+ /* just save the new compressed entry size in
+ * entry_ptr->compressed_size. We don't need to
+ * do more, as compressed size is only used for I/O.
+ */
+ HDassert(entry_ptr->compressed);
+ entry_ptr->compressed_size = new_compressed_len;
+ }
+ } /* end if ( serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET ) */
- status = (entry_ptr->type->flush)(f, primary_dxpl_id, destroy_entry,
- entry_ptr->addr, entry_ptr,
- &flush_flags);
- *first_flush_ptr = FALSE;
+ /* Serialize object into buffer */
+ {
+ size_t image_len;
- } else {
+ if ( entry_ptr->compressed )
+ image_len = entry_ptr->compressed_size;
+ else
+ image_len = entry_ptr->size;
+
+ /* reset cache_ptr->slist_changed so we can detect slist
+ * modifications in the serialize call.
+ */
+ cache_ptr->slist_changed = FALSE;
+
+
+ if ( entry_ptr->type->serialize(f, entry_ptr->image_ptr,
+ image_len,
+ (void *)entry_ptr) < 0 )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unable to serialize entry")
+ }
- status = (entry_ptr->type->flush)(f, secondary_dxpl_id,
- destroy_entry, entry_ptr->addr,
- entry_ptr, &flush_flags);
+ /* set cache_ptr->slist_change_in_serialize if the
+ * slist was modified.
+ */
+ if ( cache_ptr->slist_changed )
+ cache_ptr->slist_change_in_pre_serialize = TRUE;
+
+#if H5C_DO_MEMORY_SANITY_CHECKS
+
+ HDassert(0 == HDmemcmp(((uint8_t *)entry_ptr->image_ptr) +
+ image_len,
+ H5C_IMAGE_SANITY_VALUE,
+ H5C_IMAGE_EXTRA_SPACE));
+
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+
+ entry_ptr->image_up_to_date = TRUE;
}
+ } /* end if ( ! (entry_ptr->image_up_to_date) ) */
+
+ /* Finally, write the image to disk.
+ *
+ * Note that if either the H5C__CLASS_NO_IO_FLAG or the
+ * 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 ( ( ((entry_ptr->type->flags) & H5C__CLASS_NO_IO_FLAG) == 0 ) &&
+ ( ((entry_ptr->type->flags) & H5C__CLASS_SKIP_WRITES) == 0 ) )
+ {
+ /* If compression is not enabled, the size of the entry on
+ * disk is entry_prt->size. However if entry_ptr->compressed
+ * is TRUE, the on disk size is entry_ptr->compressed_size.
+ */
+ size_t image_size;
- if ( status < 0 ) {
+ if ( entry_ptr->compressed )
+ image_size = entry_ptr->compressed_size;
+ else
+ image_size = entry_ptr->size;
+ if ( ( H5F_block_write(f, entry_ptr->type->mem_type,
+ entry_ptr->addr,
+ image_size, dxpl_id,
+ entry_ptr->image_ptr) < 0 ) )
+ {
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
- "unable to flush entry")
+ "Can't write image to file.")
}
+ }
-#ifdef H5_HAVE_PARALLEL
- if ( flush_flags != H5C_CALLBACK__NO_FLAGS_SET ) {
-
- /* In the parallel case, flush operations 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 )
+ /* if the entry has a notify callback, notify it that we have
+ * just flushed the entry.
+ */
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "resize/move in serialize occured in parallel case.")
+ 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 ( ( entry_ptr != NULL ) && ( write_entry ) ) */
- }
-#endif /* H5_HAVE_PARALLEL */
+
+ /* 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.
+ */
+
+ if ( entry_ptr != NULL )
+ {
+ /* 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)
+ }
+ } 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)
+
+ }
+
+ if ( destroy )
+ {
+ if ( take_ownership )
+ {
+ HDassert(!destroy_entry);
+ }
+ else
+ {
+ HDassert(destroy_entry);
+ }
+
+ H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr, \
+ take_ownership)
}
- if ( ( ! destroy ) && ( entry_ptr->in_slist ) ) {
+ /* 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 ( destroy ) {
+
+ 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")
+ }
+ } /* end if */
- H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr)
+ /* Update the cache internal data structures. */
+ if ( destroy )
+ {
+ /* 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) Update the replacement policy for eviction
+ *
+ * Finally, if the destroy_entry flag is set, discard the
+ * entry.
+ */
+
+ H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr)
+
+ if ( ( entry_ptr->in_slist ) &&
+ ( del_entry_from_slist_on_destroy ) ) {
+
+ H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr)
+ }
+
+ H5C__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, FAIL)
}
+ 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)
- if ( ( ! destroy ) && ( was_dirty ) ) {
+ H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr)
+
+
+ /* 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);
+
+ if ( ( entry_ptr->type->clear != NULL ) &&
+ ( (entry_ptr->type->clear)(f, (void *)entry_ptr, FALSE) ) )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unable to clear entry")
+ }
}
- if ( ! destroy ) { /* i.e. if the entry still exists */
+ /* reset the flush_in progress flag */
- HDassert( !(entry_ptr->is_dirty) );
- HDassert( !(entry_ptr->flush_marker) );
- HDassert( !(entry_ptr->in_slist) );
- HDassert( !(entry_ptr->is_protected) );
- HDassert( !(entry_ptr->is_read_only) );
- HDassert( (entry_ptr->ro_ref_count) == 0 );
+ entry_ptr->flush_in_progress = FALSE;
- if ( (flush_flags & H5C_CALLBACK__SIZE_CHANGED_FLAG) != 0 ) {
+ } /* end if */
- /* The entry size changed as a result of the flush.
- *
- * Most likely, the entry was compressed, and the
- * new version is of a different size than the old.
- *
- * In any case, we must update entry and cache size
- * accordingly.
- */
- size_t new_size;
- if ( (entry_ptr->type->size)(f, (void *)entry_ptr, &new_size)
- < 0 ) {
+ /* Internal cache data structures should now be up to date, and
+ * consistant with the status of the entry.
+ *
+ * Now discard the entry if appropriate.
+ */
+ if ( entry_ptr != NULL )
+ {
+ if ( destroy )
+ {
+ /* start by freeing the buffer for the on disk image */
+ if(entry_ptr->image_ptr != NULL)
+ entry_ptr->image_ptr = H5MM_xfree(entry_ptr->image_ptr);
+
+ /* Check whether we should free the space in the file that
+ * the entry occupies
+ */
+ if ( free_file_space )
+ {
+
+ size_t fsf_size;
+
+ /* Sanity checks */
+ HDassert(H5F_addr_defined(entry_ptr->addr));
+ HDassert(!H5F_IS_TMP_ADDR(f, entry_ptr->addr));
+#ifndef NDEBUG
+{
+ hbool_t curr_compressed = FALSE;
+ size_t curr_len;
+ size_t curr_compressed_len = 0;
+
+ /* Get the actual image size for the thing again */
+ entry_ptr->type->image_len((void *)entry_ptr, &curr_len, &curr_compressed, &curr_compressed_len);
+ HDassert(curr_len == entry_ptr->size);
+ HDassert(curr_compressed == entry_ptr->compressed);
+ HDassert(curr_compressed_len == entry_ptr->compressed_size);
+}
+#endif /* NDEBUG */
- HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, FAIL, \
- "Can't get entry size after flush")
+ /* 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->compressed_size if
+ * entry_ptr->compressed == TRUE, and entry_ptr->size
+ * if entry_ptr->compressed == FALSE.
+ */
+ 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")
+ }
+ }
+ else if ( entry_ptr->compressed ) /* use compressed size */
+ {
+ fsf_size = entry_ptr->compressed_size;
+ }
+ else /* no file space free size callback -- use entry size */
+ {
+ fsf_size = entry_ptr->size;
}
- if ( new_size != entry_ptr->size ) {
+ /* Release the space on disk */
+ if ( H5MF_xfree(f, entry_ptr->type->mem_type, dxpl_id,
+ entry_ptr->addr, (hsize_t)fsf_size) < 0)
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, \
+ "unable to free file space for cache entry")
+ }
+ } /* end if ( free_file_space ) */
- HDassert( entry_ptr->size < H5C_MAX_ENTRY_SIZE );
- /* update the hash table for the size change
- * We pass TRUE as the was_clean parameter, as we
- * have already updated the clean and dirty index
- * size fields for the fact that the entry has
- * been flushed. (See above call to
- * H5C__UPDATE_INDEX_FOR_ENTRY_CLEAN()).
- */
- H5C__UPDATE_INDEX_FOR_SIZE_CHANGE((cache_ptr), \
- (entry_ptr->size), \
- (new_size), \
- (entry_ptr), \
- (TRUE))
-
- /* The entry can't be protected since we just flushed 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_size)
+ /* Reset the pointer to the cache the entry is within. -QAK */
+ entry_ptr->cache_ptr = NULL;
- /* The entry can't be in the slist, so no need to update
- * the slist for the size change.
- */
+ /* 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.
+ */
+ cache_ptr->last_entry_removed_ptr++;
+ cache_ptr->last_entry_removed_ptr = entry_ptr;
- /* update stats for the size change */
- H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, \
- entry_ptr, \
- new_size)
+ /* Check for actually destroying the entry in memory */
+ /* (As opposed to taking ownership of it) */
+ if ( destroy_entry )
+ {
+ /* if the entry is dirty and it has a clear callback,
+ * call this callback now. Since this callback exists,
+ * it follows tht the client maintains its own dirty bits,
+ * which must be cleared before the entry is freed to avoid
+ * sanity check failures. Also clear the dirty flag for
+ * the same reason.
+ */
- /* finally, update the entry size proper */
- entry_ptr->size = new_size;
- }
- }
+ if ( entry_ptr->is_dirty ) {
- if ( (flush_flags & H5C_CALLBACK__MOVED_FLAG) != 0 ) {
-
- /* The entry was moved as the result of the flush.
- *
- * Most likely, the entry was compressed, and the
- * new version is larger than the old and thus had
- * to be relocated.
- *
- * At preset, all processing for this case is
- * handled elsewhere. But lets keep the if statement
- * around just in case.
- */
+ entry_ptr->is_dirty = FALSE;
- }
+ if ( ( entry_ptr->type->clear != NULL ) &&
+ ( (entry_ptr->type->clear)(f, (void *)entry_ptr, TRUE) ) )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unable to clear entry")
+ }
+ }
+
+ /* 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;
- /* reset the flush_in progress flag */
- entry_ptr->flush_in_progress = FALSE;
- }
+ /* 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.")
+ }
+ }
+ 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;
+ }
+ } /* if ( destroy ) */
+ }
+
+ if ( entry_ptr != NULL )
+ {
if ( cache_ptr->log_flush ) {
status = (cache_ptr->log_flush)(cache_ptr, addr, was_dirty, flags);
@@ -8606,8 +9611,14 @@ H5C_flush_single_entry(H5F_t * f,
}
done:
- HDassert( ( destroy ) ||
+
+ HDassert( ( ret_value != SUCCEED ) || ( destroy_entry ) ||
( ( entry_ptr ) && ( ! entry_ptr->flush_in_progress ) ) );
+
+ HDassert( ( ret_value != SUCCEED ) || ( destroy_entry ) ||
+ ( take_ownership ) ||
+ ( ( entry_ptr ) && ( ! entry_ptr->is_dirty ) ) );
+
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_flush_single_entry() */
@@ -8637,8 +9648,19 @@ H5C_load_entry(H5F_t * f,
haddr_t addr,
void * udata)
{
+ hbool_t dirty = FALSE; /* Flag indicating whether thing was dirtied during deserialize */
+ hbool_t compressed = FALSE; /* flag indicating whether thing */
+ /* will be run through filters on */
+ /* on read and write. Usually FALSE */
+ /* set to true if appropriate. */
+ size_t compressed_size = 0; /* entry compressed size if */
+ /* known -- otherwise uncompressed. */
+ /* Zero indicates compression not */
+ /* enabled. */
+ void * image = NULL; /* Buffer for disk image */
void * thing = NULL; /* Pointer to thing loaded */
H5C_cache_entry_t * entry; /* Alias for thing loaded, as cache entry */
+ size_t len; /* Size of image in file */
unsigned u; /* Local index variable */
void * ret_value; /* Return value */
@@ -8648,14 +9670,310 @@ H5C_load_entry(H5F_t * f,
HDassert(f->shared);
HDassert(f->shared->cache);
HDassert(type);
- HDassert(type->load);
- HDassert(type->size);
+
+ /* verify absence of prohibited or unsupported type flag combinations */
+ HDassert(!(type->flags & H5C__CLASS_NO_IO_FLAG));
+
+ /* for now, we do not combine the speculative load and compressed flags */
+ HDassert(!((type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG) &&
+ (type->flags & H5C__CLASS_COMPRESSED_FLAG)));
+
+ /* Can't see how skip reads could be usefully combined with
+ * either the speculative read or compressed flags. Hence disallow.
+ */
+ HDassert(!((type->flags & H5C__CLASS_SKIP_READS) &&
+ (type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG)));
+ HDassert(!((type->flags & H5C__CLASS_SKIP_READS) &&
+ (type->flags & H5C__CLASS_COMPRESSED_FLAG)));
+
HDassert(H5F_addr_defined(addr));
+ HDassert(type->get_load_size);
+ HDassert(type->deserialize);
+
+ /* Call the get_load_size callback, to retrieve the initial
+ * size of image
+ */
+ if(type->get_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) {
+
+/* Quincey has added patches for eoa calculations -- leave the original
+ * code around until we see the effect of these patches.
+ * JRM -- 1/1/15
+ */
+#if 0 /* original code */ /* JRM */
+ /* the original version of this code has several problems:
+ *
+ * First, the sblock is not available until the sblock
+ * has been read in, which causes a seg fault. This is
+ * dealt with easily enough by testing to see if
+ * f->shared->sblock is NULL, and calling H5FD_get_base_addr()
+ * to obtain the base addr when it is.
+ *
+ * The second issue is more subtle. H5F_get_eoa() calls
+ * H5FD_get_eoa(). However, this function returns the EOA as
+ * a relative address -- i.e. relative to the base address.
+ * This means that the base addr + addr < eoa sanity check will
+ * fail whenever the super block is not at address 0 when
+ * reading in the first chunk of the super block.
+ *
+ * To address these issues, I have rewritten the code to
+ * simply verify that the address plus length is less than
+ * the eoa. I think this is sufficient, but further testing
+ * should tell me if it isn't.
+ * JRM -- 8/29/14
+ */
+ haddr_t eoa; /* End-of-allocation in the file */
+ haddr_t base_addr; /* Base address of file data */
+
+ /* Get the file's end-of-allocation value */
+ eoa = H5F_get_eoa(f, type->mem_type);
+ HDassert(H5F_addr_defined(eoa));
+
+ /* Get the file's base address */
+ if ( f->shared->sblock )
+
+ base_addr = H5F_BASE_ADDR(f);
+
+ else { /* sblock not loaded yet -- use file driver info */
+
+ HDassert(f->shared->lf);
+ base_addr = H5FD_get_base_addr(f->shared->lf);
+
+ }
+ HDassert(H5F_addr_defined(base_addr));
+
+ /* Check for bad address in general */
+ if((addr + base_addr) > eoa)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, \
+ "address of object past end of allocation")
+
+ /* Check if the amount of data to read will be past the eoa */
+ if((addr + base_addr + len) > eoa)
+ /* Trim down the length of the metadata */
+ len = (size_t)(eoa - (addr + base_addr));
+
+#else /* modified code */ /* JRM */
+
+ haddr_t eoa; /* End-of-allocation in the file */
+ H5FD_mem_t cooked_type;
+
+ /* 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);
+
+ HDassert(H5F_addr_defined(eoa));
+
+ /* Check for bad address in general */
+ if ( H5F_addr_gt(addr, eoa) )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, \
+ "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) ) {
+
+ /* Trim down the length of the metadata */
+
+ /* Note that for some cache clients, this will cause an
+ * assertion failure. JRM -- 8/29/14
+ */
+ len = (size_t)(eoa - addr);
+ }
+
+ if ( len <= 0 )
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, \
+ "len not positive after adjustment for EOA.")
+
+#endif /* modified code */ /* JRM */
+ }
+ /* Allocate the buffer for reading the on-disk entry image */
+ if(NULL == (image = 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
+ HDmemcpy(((uint8_t *)image) + len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+
+ /* Get the on-disk entry image */
+ if ( 0 == (type->flags & H5C__CLASS_SKIP_READS) )
+ if(H5F_block_read(f, type->mem_type, addr, len, dxpl_id, image) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_READERROR, NULL, "Can't read image*")
+
+ /* 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")
+
+ /* If the client's cache has an image_len callback, check it */
+ if(type->image_len) {
+ size_t new_len; /* New size of on-disk image */
+
+ /* set magic and type field in *entry_ptr. While the image_len
+ * callback shouldn't touch the cache specific fields, it may check
+ * these fields to ensure that it it has received the expected
+ * value.
+ *
+ * Note that this initialization is repeated below on the off
+ * chance that we had to re-try the deserialization.
+ */
+ entry = (H5C_cache_entry_t *)thing;
+ entry->magic = H5C__H5C_CACHE_ENTRY_T_MAGIC;
+ entry->type = type;
+
+ /* verify that compressed and compressed_len are initialized */
+ HDassert(compressed == FALSE);
+ HDassert(compressed_size == 0);
+
+ /* Get the actual image size for the thing */
+ if(type->image_len(thing, &new_len, &compressed, &compressed_size) < 0)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, NULL, \
+ "can't retrieve image length")
+
+ if(new_len == 0)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "image length is 0")
+
+ HDassert(((type->flags & H5C__CLASS_COMPRESSED_FLAG) != 0) ||
+ ((compressed == FALSE) && (compressed_size == 0)));
+ HDassert((compressed == TRUE) || (compressed_size == 0));
+
+ if(new_len != len) {
+
+ if(type->flags & H5C__CLASS_COMPRESSED_FLAG) {
+
+ /* if new_len != len, then compression must be
+ * enabled on the entry. In this case, the image_len
+ * callback should have set compressed to TRUE, set
+ * new_len equal to the uncompressed size of the
+ * entry, and compressed_len equal to the compressed
+ * size -- which must equal len.
+ *
+ * We can't verify the uncompressed size, but we can
+ * verify the rest with the following assertions.
+ */
+ HDassert(compressed);
+ HDassert(compressed_size == len);
+
+ /* new_len should contain the uncompressed size. Set len
+ * equal to new_len, so that the cache will use the
+ * uncompressed size for purposes of space allocation, etc.
+ */
+ len = new_len;
+
+ } else if (type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG) {
+
+ void *new_image; /* Buffer for disk image */
+
+ /* compressed must be FALSE, and compressed_size
+ * must be zero.
+ */
+ HDassert(!compressed);
+ HDassert(compressed_size == 0);
+
+ /* Adjust the size of the image to match new_len */
+ if(NULL == (new_image = H5MM_realloc(image,
+ new_len + H5C_IMAGE_EXTRA_SPACE)))
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, \
+ "image null after H5MM_realloc()")
+
+ image = new_image;
+
+#if H5C_DO_MEMORY_SANITY_CHECKS
+
+ HDmemcpy(((uint8_t *)image) + new_len,
+ H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
+
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+
+ /* If the thing's image needs to be bigger for a speculatively
+ * loaded thing, free the thing and retry with new length
+ */
+ if (new_len > len) {
+
+ /* Release previous (possibly partially initialized)
+ * thing. Note that we must set entry->magic to
+ * H5C__H5C_CACHE_ENTRY_T_BAD_MAGIC and set one or
+ * two other fields before the call to free_icr
+ * so as to avoid sanity check failures.
+ */
+ entry->magic = H5C__H5C_CACHE_ENTRY_T_BAD_MAGIC;
+
+ entry->addr = addr;
+
+ if ( type->free_icr(thing) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, NULL, \
+ "free_icr callback failed")
+
+ /* Go get the on-disk image again */
+ if(H5F_block_read(f, type->mem_type, addr,
+ new_len, dxpl_id, image) < 0)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, \
+ "Can't read image")
+
+ /* Deserialize on-disk image into native memory
+ * form again
+ */
+ if(NULL == (thing = type->deserialize(image, new_len,
+ udata, &dirty)))
- if(NULL == (thing = (type->load)(f, dxpl_id, addr, udata)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, \
+ "Can't deserialize image")
- HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "unable to load entry")
+#ifndef NDEBUG
+ {
+ /* new_compressed and new_compressed_size must be
+ * initialize to FALSE / 0 respectively, as clients
+ * that don't use compression may ignore these two
+ * parameters.
+ */
+ hbool_t new_compressed = FALSE;
+ size_t new_compressed_size = 0;
+ size_t new_new_len;
+
+ /* Get the actual image size for the thing again. Note
+ * that since this is a new thing, we have to set
+ * the magic and type fields again so as to avoid
+ * failing sanity checks.
+ */
+ entry = (H5C_cache_entry_t *)thing;
+ entry->magic = H5C__H5C_CACHE_ENTRY_T_MAGIC;
+ entry->type = type;
+
+ type->image_len(thing, &new_new_len, &new_compressed, &new_compressed_size);
+ HDassert(new_new_len == new_len);
+ HDassert(!new_compressed);
+ HDassert(new_compressed_size == 0);
+ }
+#endif /* NDEBUG */
+ } /* end if (new_len > len) */
+
+ /* Retain adjusted size */
+ len = new_len;
+ } else { /* throw an error */
+
+ HGOTO_ERROR(H5E_CACHE, H5E_UNSUPPORTED, NULL, \
+ "size of non-speculative, non-compressed object changed")
+ }
+ } /* end if (new_len != len) */
+ } /* end if */
entry = (H5C_cache_entry_t *)thing;
@@ -8663,35 +9981,39 @@ H5C_load_entry(H5F_t * f,
*
* However, when this code is used in the metadata cache, it is
* possible that object headers will be dirty at this point, as
- * the load function will alter object headers if necessary to
+ * the deserialize function will alter object headers if necessary to
* fix an old bug.
*
- * To support this bug fix, I have replace the old assert:
- *
- * HDassert( entry->is_dirty == FALSE );
- *
- * with:
+ * In the following assert:
*
- * HDassert( ( entry->is_dirty == FALSE ) || ( type->id == 5 ) );
+ * HDassert( ( dirty == FALSE ) || ( type->id == 5 || type->id == 6 ) );
*
- * Note that type id 5 is associated with object headers in the metadata
- * cache.
+ * 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( ( entry->is_dirty == FALSE ) || ( type->id == 5 ) );
-#ifndef NDEBUG
+ HDassert( ( dirty == FALSE ) || ( type->id == 5 || type->id == 6) );
+
entry->magic = H5C__H5C_CACHE_ENTRY_T_MAGIC;
-#endif /* NDEBUG */
entry->cache_ptr = f->shared->cache;
entry->addr = addr;
+ entry->size = len;
+ HDassert(entry->size < H5C_MAX_ENTRY_SIZE);
+ entry->compressed = compressed;
+ entry->compressed_size = compressed_size;
+ entry->image_ptr = image;
+ entry->image_up_to_date = TRUE;
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
@@ -8700,13 +10022,6 @@ H5C_load_entry(H5F_t * f,
#endif /* H5_HAVE_PARALLEL */
entry->flush_in_progress = FALSE;
entry->destroy_in_progress = FALSE;
- entry->free_file_space_on_destroy = FALSE;
-
- if((type->size)(f, thing, &(entry->size)) < 0)
-
- HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, NULL, "Can't get size of thing")
-
- HDassert( entry->size < H5C_MAX_ENTRY_SIZE );
/* Initialize flush dependency height fields */
entry->flush_dep_parent = NULL;
@@ -8727,6 +10042,18 @@ H5C_load_entry(H5F_t * f,
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 = H5MM_xfree(image);
+ } /* end if */
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_load_entry() */
@@ -8765,36 +10092,45 @@ done:
*
* Programmer: John Mainzer, 5/14/04
*
- * JRM -- 11/13/08
- * Modified function to always observe the min_clean_size
- * whether we are maintaining the clean and dirt LRU lists
- * or not. To do this, we had to add the new clean_index_size
- * and dirty_index_size fields to H5C_t, and supporting code
- * as needed throughout the cache.
+ * Changes: Modified function to skip over entries with the
+ * flush_in_progress flag set. If this is not done,
+ * an infinite recursion is possible if the cache is
+ * full, and the pre-serialize or serialize routine
+ * attempts to load another entry.
*
- * The purpose of this modification is to avoid "metadata
- * blizzards" in the write only case. In such instances,
- * the cache was allowed to fill with dirty metadata. When
- * we finally needed to evict an entry to make space, we had
- * to flush out a whole cache full of metadata -- which has
- * interesting performance effects. We hope to avoid (or
- * perhaps more accurately hide) this effect by maintaining
- * the min_clean_size, which should force us to start flushing
- * entries long before we actually have to evict something
- * to make space.
+ * This error was exposed by a re-factor of the
+ * H5C_flush_single_entry() routine. However, it was
+ * a potential bug from the moment that entries were
+ * allowed to load other entries on flush.
+ *
+ * In passing, note that the primary and secondary dxpls
+ * mentioned in the comment above have been replaced by
+ * a single dxpl at some point, and thus the discussion
+ * above is somewhat obsolete. Date of this change is
+ * unkown.
+ *
+ * JRM -- 12/26/14
+ *
+ * Modified function to detect deletions of entries
+ * during a scan of the LRU, and where appropriate,
+ * restart the scan to avoid proceeding with a next
+ * entry that is no longer in the cache.
+ *
+ * Note the absence of checks after flushes of clean
+ * entries. As a second entry can only be removed by
+ * by a call to the pre_serialize or serialize callback
+ * of the first, and as these callbacks will not be called
+ * on clean entries, no checks are needed.
+ *
+ * JRM -- 4/6/15
*
- * MAM -- 01/06/09
- * Added code to maintain clean_entries_skipped and total_entries
- * scanned statistics.
*-------------------------------------------------------------------------
*/
static herr_t
H5C_make_space_in_cache(H5F_t * f,
- hid_t primary_dxpl_id,
- hid_t secondary_dxpl_id,
+ hid_t dxpl_id,
size_t space_needed,
- hbool_t write_permitted,
- hbool_t * first_flush_ptr)
+ hbool_t write_permitted)
{
H5C_t * cache_ptr = f->shared->cache;
herr_t result;
@@ -8807,6 +10143,7 @@ H5C_make_space_in_cache(H5F_t * f,
size_t empty_space;
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;
@@ -8817,13 +10154,12 @@ H5C_make_space_in_cache(H5F_t * f,
HDassert( f );
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
- HDassert( first_flush_ptr != NULL );
- HDassert( ( *first_flush_ptr == TRUE ) || ( *first_flush_ptr == FALSE ) );
HDassert( cache_ptr->index_size ==
(cache_ptr->clean_index_size + cache_ptr->dirty_index_size) );
if ( write_permitted ) {
+ restart_scan = FALSE;
initial_list_len = cache_ptr->LRU_list_len;
entry_ptr = cache_ptr->LRU_tail_ptr;
@@ -8854,6 +10190,7 @@ H5C_make_space_in_cache(H5F_t * f,
( 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 );
@@ -8866,7 +10203,8 @@ H5C_make_space_in_cache(H5F_t * f,
prev_is_dirty = prev_ptr->is_dirty;
}
- if ( (entry_ptr->type)->id != H5C__EPOCH_MARKER_TYPE ) {
+ if ( ( (entry_ptr->type)->id != H5C__EPOCH_MARKER_TYPE ) &&
+ ( ! entry_ptr->flush_in_progress ) ) {
didnt_flush_entry = FALSE;
@@ -8881,14 +10219,29 @@ H5C_make_space_in_cache(H5F_t * f,
}
#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;
+
result = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- entry_ptr->type,
+ dxpl_id,
entry_ptr->addr,
H5C__NO_FLAGS_SET,
- first_flush_ptr,
- FALSE);
+ FALSE,
+ NULL);
+
+ 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 ) {
@@ -8897,13 +10250,12 @@ H5C_make_space_in_cache(H5F_t * f,
#endif /* H5C_COLLECT_CACHE_STATS */
result = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- entry_ptr->type,
+ dxpl_id,
entry_ptr->addr,
H5C__FLUSH_INVALIDATE_FLAG,
- first_flush_ptr,
- TRUE);
+ TRUE,
+ NULL);
+
} else {
/* We have enough space so don't flush clean entry.
@@ -8924,7 +10276,8 @@ H5C_make_space_in_cache(H5F_t * f,
} else {
- /* Skip epoch markers. Set result to SUCCEED to avoid
+ /* Skip epoch markers and entries that are in the process
+ * of being flushed. Set result to SUCCEED to avoid
* triggering the error code below.
*/
didnt_flush_entry = TRUE;
@@ -8938,26 +10291,20 @@ H5C_make_space_in_cache(H5F_t * f,
}
if ( prev_ptr != NULL ) {
-#ifndef NDEBUG
- if ( prev_ptr->magic != H5C__H5C_CACHE_ENTRY_T_MAGIC ) {
-
- /* something horrible has happened to *prev_ptr --
- * scream and die.
- */
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "*prev_ptr corrupt 1")
- }
-#endif /* NDEBUG */
if ( didnt_flush_entry ) {
- /* epoch markers don't get flushed, so the sanity checks
- * on normal entries will fail -- thus just set entry_ptr
- * to prev_ptr and go on.
+ /* 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 ( ( prev_ptr->is_dirty != prev_is_dirty )
+ } else if ( ( restart_scan )
+ ||
+ ( prev_ptr->is_dirty != prev_is_dirty )
||
( prev_ptr->next != next_ptr )
||
@@ -8968,7 +10315,9 @@ H5C_make_space_in_cache(H5F_t * f,
/* 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 {
@@ -9056,13 +10405,11 @@ H5C_make_space_in_cache(H5F_t * f,
prev_ptr = entry_ptr->aux_prev;
result = H5C_flush_single_entry(f,
- primary_dxpl_id,
- secondary_dxpl_id,
- entry_ptr->type,
+ dxpl_id,
entry_ptr->addr,
H5C__FLUSH_INVALIDATE_FLAG,
- first_flush_ptr,
- TRUE);
+ TRUE,
+ NULL);
if ( result < 0 ) {
@@ -9070,6 +10417,12 @@ H5C_make_space_in_cache(H5F_t * f,
"unable to flush entry")
}
+ /* 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++;
}
@@ -9482,6 +10835,66 @@ done:
/*-------------------------------------------------------------------------
*
+ * 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_get_entry_ptr_from_addr()
*
* Purpose: Debugging function that attempts to look up an entry in the
@@ -9784,7 +11197,7 @@ done:
*-------------------------------------------------------------------------
*/
static herr_t
-H5C_flush_tagged_entries(H5F_t * f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, H5C_t * cache_ptr, haddr_t tag)
+H5C_flush_tagged_entries(H5F_t * f, hid_t dxpl_id, H5C_t * cache_ptr, haddr_t tag)
{
herr_t ret_value = SUCCEED;
@@ -9800,7 +11213,7 @@ H5C_flush_tagged_entries(H5F_t * f, hid_t primary_dxpl_id, hid_t secondary_dxpl_
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't mark tagged entries")
/* Flush all marked entries */
- if(H5C_flush_marked_entries(f, primary_dxpl_id, secondary_dxpl_id, cache_ptr) < 0)
+ if(H5C_flush_marked_entries(f, dxpl_id, cache_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't flush marked entries")
done:
@@ -9873,7 +11286,7 @@ H5C_mark_tagged_entries(H5C_t * cache_ptr, haddr_t tag)
*-------------------------------------------------------------------------
*/
static herr_t
-H5C_flush_marked_entries(H5F_t * f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, H5C_t * cache_ptr)
+H5C_flush_marked_entries(H5F_t * f, hid_t dxpl_id, H5C_t * cache_ptr)
{
herr_t ret_value = SUCCEED;
@@ -9885,8 +11298,7 @@ H5C_flush_marked_entries(H5F_t * f, hid_t primary_dxpl_id, hid_t secondary_dxpl_
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
/* Flush all marked entries */
- if(H5C_flush_cache(f, primary_dxpl_id, secondary_dxpl_id,
- H5C__FLUSH_MARKED_ENTRIES_FLAG | H5C__FLUSH_IGNORE_PROTECTED_FLAG) < 0)
+ if(H5C_flush_cache(f, dxpl_id, H5C__FLUSH_MARKED_ENTRIES_FLAG | H5C__FLUSH_IGNORE_PROTECTED_FLAG) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't flush cache")
done:
@@ -9929,7 +11341,7 @@ H5C_verify_tag(int id, haddr_t tag)
* constraints are met. */
/* Superblock */
- if(id == H5AC_SUPERBLOCK_ID) {
+ if((id == H5AC_SUPERBLOCK_ID) || (id == H5AC_DRVRINFO_ID)) {
if(tag != H5AC__SUPERBLOCK_TAG)
HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "superblock not tagged with H5AC__SUPERBLOCK_TAG")
}
@@ -10001,10 +11413,9 @@ H5C_retag_copied_metadata(H5C_t * cache_ptr, haddr_t metadata_tag)
next_entry_ptr = cache_ptr->index[u];
while(next_entry_ptr != NULL) {
- if(cache_ptr->index[u] != NULL) {
+ if(cache_ptr->index[u] != NULL)
if((cache_ptr->index[u])->tag == H5AC__COPIED_TAG)
(cache_ptr->index[u])->tag = metadata_tag;
- } /* end if */
next_entry_ptr = next_entry_ptr->ht_next;
} /* end while */