summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/H5AC2.c4790
-rw-r--r--src/H5AC2pkg.h335
-rw-r--r--src/H5AC2private.h315
-rw-r--r--src/H5AC2public.h393
-rw-r--r--src/H5C2.c11024
-rw-r--r--src/H5C2pkg.h949
-rw-r--r--src/H5C2private.h1350
-rw-r--r--src/H5C2public.h55
8 files changed, 19211 insertions, 0 deletions
diff --git a/src/H5AC2.c b/src/H5AC2.c
new file mode 100644
index 0000000..a053b36
--- /dev/null
+++ b/src/H5AC2.c
@@ -0,0 +1,4790 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+ * Copyright by the Board of Trustees of the University of Illinois. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5AC2.c
+ * Jul 9 1997
+ * Robb Matzke <matzke@llnl.gov>
+ *
+ * Purpose: Functions in this file implement a cache for
+ * things which exist on disk. All "things" associated
+ * with a particular HDF file share the same cache; each
+ * HDF file has it's own cache.
+ *
+ * Modifications:
+ *
+ * Robb Matzke, 4 Aug 1997
+ * Added calls to H5E.
+ *
+ * Quincey Koziol, 22 Apr 2000
+ * Turned on "H5AC2_SORT_BY_ADDR"
+ *
+ * John Mainzer, 5/19/04
+ * Complete redesign and rewrite. See the header comments for
+ * H5AC2_t for an overview of what is going on.
+ *
+ * John Mainzer, 6/4/04
+ * Factored the new cache code into a separate file (H5C.c) to
+ * facilitate re-use. Re-worked this file again to use H5C.
+ *
+ * John Mainzer, 10/18/07
+ * Copied H5AC2.c to H5AC22.c and reworked to use H5C2 instead of H5C.
+ * All this is in support of cache API modifications needed for
+ * journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5C2_PACKAGE /*suppress error about including H5C2pkg */
+#define H5AC2_PACKAGE /*suppress error about including H5AC2pkg */
+#define H5F_PACKAGE /*suppress error about including H5Fpkg */
+
+/* Interface initialization */
+#define H5_INTERFACE_INIT_FUNC H5AC2_init_interface
+
+#ifdef H5_HAVE_PARALLEL
+#include <mpi.h>
+#endif /* H5_HAVE_PARALLEL */
+
+#include "H5private.h" /* Generic Functions */
+#include "H5AC2pkg.h" /* Metadata cache */
+#include "H5C2pkg.h" /* Cache */
+#include "H5Dprivate.h" /* Dataset functions */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5Fpkg.h" /* Files */
+#include "H5FDprivate.h" /* File drivers */
+#include "H5FLprivate.h" /* Free Lists */
+#include "H5Iprivate.h" /* IDs */
+#include "H5MMprivate.h" /* Memory management */
+#include "H5Pprivate.h" /* Property lists */
+
+
+#ifdef H5_HAVE_PARALLEL
+
+/* Declare a free list to manage the H5AC2_aux_t struct */
+H5FL_DEFINE_STATIC(H5AC2_aux_t);
+
+#endif /* H5_HAVE_PARALLEL */
+
+/****************************************************************************
+ *
+ * structure H5AC2_slist_entry_t
+ *
+ * The dirty entry list maintained via the d_slist_ptr field of H5AC2_aux_t
+ * and the cleaned entry list maintained via the c_slist_ptr field of
+ * H5AC2_aux_t are just lists of the file offsets of the dirty/cleaned
+ * entries. Unfortunately, the slist code makes us define a dynamically
+ * allocated structure to store these offsets in. This structure serves
+ * that purpose. Its fields are as follows:
+ *
+ * magic: Unsigned 32 bit integer always set to
+ * H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC. This field is used to
+ * validate pointers to instances of H5AC2_slist_entry_t.
+ *
+ * addr: file offset of a metadata entry. Entries are added to this
+ * list (if they aren't there already) when they are marked
+ * dirty in an unprotect, inserted, or renamed. They are
+ * removed when they appear in a clean entries broadcast.
+ *
+ ****************************************************************************/
+
+#ifdef H5_HAVE_PARALLEL
+
+#define H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC 0x00D0A02
+
+typedef struct H5AC2_slist_entry_t
+{
+ uint32_t magic;
+
+ haddr_t addr;
+} H5AC2_slist_entry_t;
+
+/* Declare a free list to manage the H5AC2_slist_entry_t struct */
+H5FL_DEFINE_STATIC(H5AC2_slist_entry_t);
+
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*
+ * Private file-scope variables.
+ */
+
+/* Default dataset transfer property list for metadata I/O calls */
+/* (Collective set, "block before metadata write" set and "library internal" set) */
+/* (Global variable definition, declaration is in H5AC2private.h also) */
+hid_t H5AC2_dxpl_id=(-1);
+
+/* Private dataset transfer property list for metadata I/O calls */
+/* (Collective set and "library internal" set) */
+/* (Static variable definition) */
+static hid_t H5AC2_noblock_dxpl_id=(-1);
+
+/* Dataset transfer property list for independent metadata I/O calls */
+/* (just "library internal" set - i.e. independent transfer mode) */
+/* (Global variable definition, declaration is in H5AC2private.h also) */
+hid_t H5AC2_ind_dxpl_id=(-1);
+
+
+/*
+ * Private file-scope function declarations:
+ */
+
+static herr_t H5AC2_check_if_write_permitted(const H5F_t *f,
+ hid_t dxpl_id,
+ hbool_t * write_permitted_ptr);
+
+#ifdef H5_HAVE_PARALLEL
+static herr_t H5AC2_broadcast_clean_list(H5AC2_t * cache_ptr);
+#endif /* JRM */
+
+static herr_t H5AC2_ext_config_2_int_config(
+ H5AC2_cache_config_t * ext_conf_ptr,
+ H5C2_auto_size_ctl_t * int_conf_ptr);
+
+#ifdef H5_HAVE_PARALLEL
+static herr_t H5AC2_log_deleted_entry(H5AC2_t * cache_ptr,
+ H5AC2_info_t * entry_ptr,
+ haddr_t addr,
+ unsigned int flags);
+
+static herr_t H5AC2_log_dirtied_entry(H5AC2_t * cache_ptr,
+ H5C2_cache_entry_t * entry_ptr,
+ haddr_t addr,
+ hbool_t size_changed,
+ size_t new_size);
+
+static herr_t H5AC2_log_flushed_entry(H5C2_t * cache_ptr,
+ haddr_t addr,
+ hbool_t was_dirty,
+ unsigned flags,
+ int type_id);
+
+#if 0 /* this is useful debugging code -- JRM */
+static herr_t H5AC2_log_flushed_entry_dummy(H5C2_t * cache_ptr,
+ haddr_t addr,
+ hbool_t was_dirty,
+ unsigned flags,
+ int type_id);
+#endif /* JRM */
+
+static herr_t H5AC2_log_inserted_entry(H5F_t * f,
+ H5AC2_t * cache_ptr,
+ H5AC2_info_t * entry_ptr,
+ const H5AC2_class_t * type,
+ haddr_t addr,
+ size_t size);
+
+static herr_t H5AC2_propagate_flushed_and_still_clean_entries_list(H5F_t * f,
+ hid_t dxpl_id,
+ H5AC2_t * cache_ptr,
+ hbool_t do_barrier);
+
+static herr_t H5AC2_receive_and_apply_clean_list(H5F_t * f,
+ hid_t dxpl_id,
+ H5AC2_t * cache_ptr);
+
+static herr_t H5AC2_log_renamed_entry(H5AC2_t * cache_ptr,
+ haddr_t old_addr,
+ haddr_t new_addr);
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_init
+ *
+ * Purpose: Initialize the interface from some other layer.
+ *
+ * Return: Success: non-negative
+ *
+ * Failure: negative
+ *
+ * Programmer: Quincey Koziol
+ * Saturday, January 18, 2003
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_init(void)
+{
+ herr_t ret_value=SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5AC2_init, FAIL)
+ /* FUNC_ENTER() does all the work */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_init_interface
+ *
+ * Purpose: Initialize interface-specific information
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * Thursday, July 18, 2002
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5AC2_init_interface(void)
+{
+#ifdef H5_HAVE_PARALLEL
+ H5P_genclass_t *xfer_pclass; /* Dataset transfer property list class object */
+ H5P_genplist_t *xfer_plist; /* Dataset transfer property list object */
+ unsigned block_before_meta_write; /* "block before meta write" property value */
+ unsigned library_internal=1; /* "library internal" property value */
+ H5FD_mpio_xfer_t xfer_mode; /* I/O transfer mode property value */
+ herr_t ret_value=SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT(H5AC2_init_interface)
+
+ /* Sanity check */
+ HDassert(H5P_CLS_DATASET_XFER_g!=(-1));
+
+ /* Get the dataset transfer property list class object */
+ if (NULL == (xfer_pclass = H5I_object(H5P_CLS_DATASET_XFER_g)))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADATOM, FAIL, "can't get property list class")
+
+ /* Get an ID for the blocking, collective H5AC2 dxpl */
+ if ((H5AC2_dxpl_id=H5P_create_id(xfer_pclass)) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "unable to register property list")
+
+ /* Get the property list object */
+ if (NULL == (xfer_plist = H5I_object(H5AC2_dxpl_id)))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADATOM, FAIL, "can't get new property list object")
+
+ /* Insert 'block before metadata write' property */
+ block_before_meta_write=1;
+ if(H5P_insert(xfer_plist,H5AC2_BLOCK_BEFORE_META_WRITE_NAME,H5AC2_BLOCK_BEFORE_META_WRITE_SIZE,&block_before_meta_write,NULL,NULL,NULL,NULL,NULL,NULL)<0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't insert metadata cache dxpl property")
+
+ /* Insert 'library internal' property */
+ if(H5P_insert(xfer_plist,H5AC2_LIBRARY_INTERNAL_NAME,H5AC2_LIBRARY_INTERNAL_SIZE,&library_internal,NULL,NULL,NULL,NULL,NULL,NULL)<0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't insert metadata cache dxpl property")
+
+ /* Set the transfer mode */
+ xfer_mode=H5FD_MPIO_COLLECTIVE;
+ if (H5P_set(xfer_plist,H5D_XFER_IO_XFER_MODE_NAME,&xfer_mode)<0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "unable to set value")
+
+ /* Get an ID for the non-blocking, collective H5AC2 dxpl */
+ if ((H5AC2_noblock_dxpl_id=H5P_create_id(xfer_pclass)) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "unable to register property list")
+
+ /* Get the property list object */
+ if (NULL == (xfer_plist = H5I_object(H5AC2_noblock_dxpl_id)))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADATOM, FAIL, "can't get new property list object")
+
+ /* Insert 'block before metadata write' property */
+ block_before_meta_write=0;
+ if(H5P_insert(xfer_plist,H5AC2_BLOCK_BEFORE_META_WRITE_NAME,H5AC2_BLOCK_BEFORE_META_WRITE_SIZE,&block_before_meta_write,NULL,NULL,NULL,NULL,NULL,NULL)<0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't insert metadata cache dxpl property")
+
+ /* Insert 'library internal' property */
+ if(H5P_insert(xfer_plist,H5AC2_LIBRARY_INTERNAL_NAME,H5AC2_LIBRARY_INTERNAL_SIZE,&library_internal,NULL,NULL,NULL,NULL,NULL,NULL)<0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't insert metadata cache dxpl property")
+
+ /* Set the transfer mode */
+ xfer_mode=H5FD_MPIO_COLLECTIVE;
+ if (H5P_set(xfer_plist,H5D_XFER_IO_XFER_MODE_NAME,&xfer_mode)<0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "unable to set value")
+
+ /* Get an ID for the non-blocking, independent H5AC2 dxpl */
+ if ((H5AC2_ind_dxpl_id=H5P_create_id(xfer_pclass)) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "unable to register property list")
+
+ /* Get the property list object */
+ if (NULL == (xfer_plist = H5I_object(H5AC2_ind_dxpl_id)))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADATOM, FAIL, "can't get new property list object")
+
+ /* Insert 'block before metadata write' property */
+ block_before_meta_write=0;
+ if(H5P_insert(xfer_plist,H5AC2_BLOCK_BEFORE_META_WRITE_NAME,H5AC2_BLOCK_BEFORE_META_WRITE_SIZE,&block_before_meta_write,NULL,NULL,NULL,NULL,NULL,NULL)<0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't insert metadata cache dxpl property")
+
+ /* Insert 'library internal' property */
+ if(H5P_insert(xfer_plist,H5AC2_LIBRARY_INTERNAL_NAME,H5AC2_LIBRARY_INTERNAL_SIZE,&library_internal,NULL,NULL,NULL,NULL,NULL,NULL)<0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't insert metadata cache dxpl property")
+
+ /* Set the transfer mode */
+ xfer_mode=H5FD_MPIO_INDEPENDENT;
+ if (H5P_set(xfer_plist,H5D_XFER_IO_XFER_MODE_NAME,&xfer_mode)<0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "unable to set value")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+#else /* H5_HAVE_PARALLEL */
+ FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5AC2_init_interface)
+
+ /* Sanity check */
+ assert(H5P_LST_DATASET_XFER_g!=(-1));
+
+ H5AC2_dxpl_id=H5P_DATASET_XFER_DEFAULT;
+ H5AC2_noblock_dxpl_id=H5P_DATASET_XFER_DEFAULT;
+ H5AC2_ind_dxpl_id=H5P_DATASET_XFER_DEFAULT;
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+#endif /* H5_HAVE_PARALLEL */
+} /* end H5AC2_init_interface() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_term_interface
+ *
+ * Purpose: Terminate this interface.
+ *
+ * Return: Success: Positive if anything was done that might
+ * affect other interfaces; zero otherwise.
+ *
+ * Failure: Negative.
+ *
+ * Programmer: Quincey Koziol
+ * Thursday, July 18, 2002
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+int
+H5AC2_term_interface(void)
+{
+ int n=0;
+
+ FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5AC2_term_interface)
+
+ if (H5_interface_initialize_g) {
+#ifdef H5_HAVE_PARALLEL
+ if(H5AC2_dxpl_id>0 || H5AC2_noblock_dxpl_id>0 || H5AC2_ind_dxpl_id>0) {
+ /* Indicate more work to do */
+ n = 1; /* H5I */
+
+ /* Close H5AC2 dxpl */
+ if (H5I_dec_ref(H5AC2_dxpl_id) < 0 ||
+ H5I_dec_ref(H5AC2_noblock_dxpl_id) < 0 ||
+ H5I_dec_ref(H5AC2_ind_dxpl_id) < 0)
+ H5E_clear_stack(NULL); /*ignore error*/
+ else {
+ /* Reset static IDs */
+ H5AC2_dxpl_id=(-1);
+ H5AC2_noblock_dxpl_id=(-1);
+ H5AC2_ind_dxpl_id=(-1);
+
+ /* Reset interface initialization flag */
+ H5_interface_initialize_g = 0;
+ } /* end else */
+ } /* end if */
+ else
+#else /* H5_HAVE_PARALLEL */
+ /* Reset static IDs */
+ H5AC2_dxpl_id=(-1);
+ H5AC2_noblock_dxpl_id=(-1);
+ H5AC2_ind_dxpl_id=(-1);
+
+#endif /* H5_HAVE_PARALLEL */
+ /* Reset interface initialization flag */
+ H5_interface_initialize_g = 0;
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(n)
+} /* end H5AC2_term_interface() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_create
+ *
+ * Purpose: Initialize the cache just after a file is opened. The
+ * SIZE_HINT is the number of cache slots desired. If you
+ * pass an invalid value then H5AC2_NSLOTS is used. You can
+ * turn off caching by using 1 for the SIZE_HINT value.
+ *
+ * Return: Success: Number of slots actually used.
+ *
+ * Failure: Negative
+ *
+ * Programmer: Robb Matzke
+ * matzke@llnl.gov
+ * Jul 9 1997
+ *
+ * Modifications:
+ *
+ * Complete re-design and re-write to support the re-designed
+ * metadata cache.
+ *
+ * At present, the size_hint is ignored, and the
+ * max_cache_size and min_clean_size fields are hard
+ * coded. This should be fixed, but a parameter
+ * list change will be required, so I will leave it
+ * for now.
+ *
+ * Since no-one seems to care, the function now returns
+ * one on success.
+ * JRM - 4/28/04
+ *
+ * Reworked the function again after abstracting its guts to
+ * the similar function in H5C2.c. The function is now a
+ * wrapper for H5C2_create().
+ * JRM - 6/4/04
+ *
+ * Deleted the old size_hint parameter and added the
+ * max_cache_size, and min_clean_size parameters.
+ *
+ * JRM - 3/10/05
+ *
+ * Deleted the max_cache_size, and min_clean_size parameters,
+ * and added the config_ptr parameter. Added code to
+ * validate the resize configuration before we do anything.
+ *
+ * JRM - 3/24/05
+ *
+ * Changed the type of config_ptr from H5AC2_auto_size_ctl_t *
+ * to H5AC2_cache_config_t *. Propagated associated changes
+ * through the function.
+ * JRM - 4/7/05
+ *
+ * Added code allocating and initializing the auxilary
+ * structure (an instance of H5AC2_aux_t), and linking it
+ * to the instance of H5C2_t created by H5C2_create(). At
+ * present, the auxilary structure is only used in PHDF5.
+ *
+ * JRM - 6/28/05
+ *
+ * Added code to set the prefix if required.
+ *
+ * JRM - 1/20/06
+ *
+ * Added code to initialize the new write_done field.
+ *
+ * JRM - 5/11/06
+ *
+ * Reworked code to conform with changes in the cache
+ * API.
+ * JRM - 10/18/07
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static const char * H5AC2_entry_type_names[H5AC2_NTYPES] =
+{
+ "B-tree nodes",
+ "symbol table nodes",
+ "local heaps",
+ "global heaps",
+ "object headers",
+ "v2 B-tree headers",
+ "v2 B-tree internal nodes",
+ "v2 B-tree leaf nodes",
+ "fractal heap headers",
+ "fractal heap direct blocks",
+ "fractal heap indirect blocks",
+ "free space headers",
+ "free space sections",
+ "shared OH message master table",
+ "shared OH message index",
+ "test entry" /* for testing only -- not used for actual files */
+};
+
+herr_t
+H5AC2_create(const H5F_t *f,
+ H5AC2_cache_config_t *config_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ herr_t result;
+#ifdef H5_HAVE_PARALLEL
+ char prefix[H5C2__PREFIX_LEN] = "";
+ MPI_Comm mpi_comm = MPI_COMM_NULL;
+ int mpi_rank = -1;
+ int mpi_size = -1;
+ H5AC2_aux_t * aux_ptr = NULL;
+#endif /* H5_HAVE_PARALLEL */
+
+ FUNC_ENTER_NOAPI(H5AC2_create, FAIL)
+
+ HDassert ( f );
+ HDassert ( NULL == f->shared->cache2 );
+ HDassert ( config_ptr != NULL ) ;
+ HDassert ( NELMTS(H5AC2_entry_type_names) == H5AC2_NTYPES);
+ HDassert ( H5C2__MAX_NUM_TYPE_IDS == H5AC2_NTYPES);
+
+ result = H5AC2_validate_config(config_ptr);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Bad cache configuration");
+ }
+
+#ifdef H5_HAVE_PARALLEL
+ if ( IS_H5FD_MPI(f) ) {
+
+ if ( (mpi_comm = H5F_mpi_get_comm(f)) == MPI_COMM_NULL ) {
+
+ HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, \
+ "can't get MPI communicator")
+ }
+
+ if ( (mpi_rank = H5F_mpi_get_rank(f)) < 0 ) {
+
+ HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "can't get mpi rank")
+ }
+
+ if ( (mpi_size = H5F_mpi_get_size(f)) < 0 ) {
+
+ HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "can't get mpi size")
+ }
+
+ /* There is no point in setting up the auxilary structure if size
+ * is less than or equal to 1, as there will never be any processes
+ * to broadcast the clean lists to.
+ */
+ if ( mpi_size > 1 ) {
+
+ if ( NULL == (aux_ptr = H5FL_CALLOC(H5AC2_aux_t)) ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "Can't allocate H5AC2 auxilary structure.")
+
+ } else {
+
+ aux_ptr->magic = H5AC2__H5AC2_AUX_T_MAGIC;
+ aux_ptr->mpi_comm = mpi_comm;
+ aux_ptr->mpi_rank = mpi_rank;
+ aux_ptr->mpi_size = mpi_size;
+ aux_ptr->write_permitted = FALSE;
+ aux_ptr->dirty_bytes_threshold =
+ H5AC2__DEFAULT_DIRTY_BYTES_THRESHOLD;
+ aux_ptr->dirty_bytes = 0;
+#if H5AC2_DEBUG_DIRTY_BYTES_CREATION
+ aux_ptr->dirty_bytes_propagations = 0;
+ aux_ptr->unprotect_dirty_bytes = 0;
+ aux_ptr->unprotect_dirty_bytes_updates = 0;
+ aux_ptr->insert_dirty_bytes = 0;
+ aux_ptr->insert_dirty_bytes_updates = 0;
+ aux_ptr->rename_dirty_bytes = 0;
+ aux_ptr->rename_dirty_bytes_updates = 0;
+#endif /* H5AC2_DEBUG_DIRTY_BYTES_CREATION */
+ aux_ptr->d_slist_ptr = NULL;
+ aux_ptr->d_slist_len = 0;
+ aux_ptr->c_slist_ptr = NULL;
+ aux_ptr->c_slist_len = 0;
+ aux_ptr->write_done = NULL;
+
+ sprintf(prefix, "%d:", mpi_rank);
+ }
+
+ if ( mpi_rank == 0 ) {
+
+ aux_ptr->d_slist_ptr =
+ H5SL_create(H5SL_TYPE_HADDR,0.5,(size_t)16);
+
+ if ( aux_ptr->d_slist_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL,
+ "can't create dirtied entry list.")
+ }
+
+ aux_ptr->c_slist_ptr =
+ H5SL_create(H5SL_TYPE_HADDR,0.5,(size_t)16);
+
+ if ( aux_ptr->c_slist_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL,
+ "can't create cleaned entry list.")
+ }
+ }
+ }
+
+ if ( aux_ptr != NULL ) {
+
+ if ( aux_ptr->mpi_rank == 0 ) {
+
+ f->shared->cache2 = H5C2_create(f,
+ H5AC2__DEFAULT_MAX_CACHE_SIZE,
+ H5AC2__DEFAULT_MIN_CLEAN_SIZE,
+ (H5AC2_NTYPES - 1),
+ (const char **)H5AC2_entry_type_names,
+ H5AC2_check_if_write_permitted,
+ TRUE,
+ H5AC2_log_flushed_entry,
+ (void *)aux_ptr);
+
+ } else {
+
+ f->shared->cache2 = H5C2_create(f,
+ H5AC2__DEFAULT_MAX_CACHE_SIZE,
+ H5AC2__DEFAULT_MIN_CLEAN_SIZE,
+ (H5AC2_NTYPES - 1),
+ (const char **)H5AC2_entry_type_names,
+ NULL,
+ FALSE,
+#if 0 /* this is useful debugging code -- keep it for a while */ /* JRM */
+ H5AC2_log_flushed_entry_dummy,
+#else /* JRM */
+ NULL,
+#endif /* JRM */
+ (void *)aux_ptr);
+ }
+
+ } else {
+
+ f->shared->cache2 = H5C2_create(f,
+ H5AC2__DEFAULT_MAX_CACHE_SIZE,
+ H5AC2__DEFAULT_MIN_CLEAN_SIZE,
+ (H5AC2_NTYPES - 1),
+ (const char **)H5AC2_entry_type_names,
+ H5AC2_check_if_write_permitted,
+ TRUE,
+ NULL,
+ NULL);
+ }
+ } else {
+#endif /* H5_HAVE_PARALLEL */
+ /* The default max cache size and min clean size will frequently be
+ * overwritten shortly by the subsequent set resize config call.
+ * -- JRM
+ */
+
+ f->shared->cache2 = H5C2_create(f,
+ H5AC2__DEFAULT_MAX_CACHE_SIZE,
+ H5AC2__DEFAULT_MIN_CLEAN_SIZE,
+ (H5AC2_NTYPES - 1),
+ (const char **)H5AC2_entry_type_names,
+ H5AC2_check_if_write_permitted,
+ TRUE,
+ NULL,
+ NULL);
+#ifdef H5_HAVE_PARALLEL
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ if ( NULL == f->shared->cache2 ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed")
+
+ }
+#ifdef H5_HAVE_PARALLEL
+ else if ( aux_ptr != NULL ) {
+
+ result = H5C2_set_prefix(f->shared->cache2, prefix);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "H5C2_set_prefix() failed")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ result = H5AC2_set_cache_auto_resize_config(f->shared->cache2, config_ptr);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "auto resize configuration failed")
+ }
+
+done:
+
+#ifdef H5_HAVE_PARALLEL
+
+ /* if there is a failure, try to tidy up the auxilary structure */
+
+ if ( ret_value != SUCCEED ) {
+
+ if ( aux_ptr != NULL ) {
+
+ if ( aux_ptr->d_slist_ptr != NULL ) {
+
+ H5SL_close(aux_ptr->d_slist_ptr);
+ }
+
+ if ( aux_ptr->c_slist_ptr != NULL ) {
+
+ H5SL_close(aux_ptr->c_slist_ptr);
+ }
+
+ aux_ptr->magic = 0;
+ H5FL_FREE(H5AC2_aux_t, aux_ptr);
+ aux_ptr = NULL;
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_create() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_dest
+ *
+ * Purpose: Flushes all data to disk and destroys the cache.
+ * This function fails if any object are protected since the
+ * resulting file might not be consistent.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Robb Matzke
+ * matzke@llnl.gov
+ * Jul 9 1997
+ *
+ * Modifications:
+ *
+ * Complete re-design and re-write to support the re-designed
+ * metadata cache.
+ * JRM - 5/12/04
+ *
+ * Abstracted the guts of the function to H5C_dest() in H5C.c,
+ * and then re-wrote the function as a wrapper for H5C_dest().
+ *
+ * JRM - 6/7/04
+ *
+ * Added code to free the auxiliary structure and its
+ * associated slist if present.
+ * JRM - 6/28/05
+ *
+ * Added code to close the trace file if it is present.
+ *
+ * JRM - 6/8/06
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_dest(H5F_t *f, hid_t dxpl_id)
+{
+ H5AC2_t *cache = NULL;
+ herr_t ret_value=SUCCEED; /* Return value */
+#ifdef H5_HAVE_PARALLEL
+ H5AC2_aux_t * aux_ptr = NULL;
+#endif /* H5_HAVE_PARALLEL */
+
+ FUNC_ENTER_NOAPI(H5AC2_dest, FAIL)
+
+ assert(f);
+ assert(f->shared->cache2);
+ cache = f->shared->cache2;
+#ifdef H5_HAVE_PARALLEL
+ aux_ptr = cache->aux_ptr;
+
+ if ( aux_ptr != NULL ) {
+
+ HDassert ( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( H5AC2_close_trace_file(cache) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5AC2_close_trace_file() failed.")
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ if ( H5C2_dest(cache, dxpl_id) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "can't destroy cache")
+ }
+
+ f->shared->cache2 = NULL;
+
+#ifdef H5_HAVE_PARALLEL
+ if ( aux_ptr != NULL ) {
+
+ if ( aux_ptr->d_slist_ptr != NULL ) {
+
+ H5SL_close(aux_ptr->d_slist_ptr);
+ }
+
+ if ( aux_ptr->c_slist_ptr != NULL ) {
+
+ H5SL_close(aux_ptr->c_slist_ptr);
+ }
+
+ aux_ptr->magic = 0;
+ H5FL_FREE(H5AC2_aux_t, aux_ptr);
+ aux_ptr = NULL;
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_dest() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_expunge_entry
+ *
+ * Purpose: Expunge the target entry from the cache without writing it
+ * to disk even if it is dirty. The entry must not be either
+ * pinned or protected.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/30/06
+ *
+ * Modifications:
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_expunge_entry(H5F_t *f,
+ hid_t dxpl_id,
+ const H5AC2_class_t *type,
+ haddr_t addr)
+{
+ herr_t result;
+ herr_t ret_value=SUCCEED; /* Return value */
+ H5AC2_t * cache_ptr = NULL;
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_expunge_entry, FAIL)
+
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(f->shared->cache2);
+ HDassert(type);
+ HDassert(H5F_addr_defined(addr));
+
+ cache_ptr = f->shared->cache2;
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the expunge entry call, only the addr, and type id are really
+ * necessary in the trace file. Write the return value to catch occult
+ * errors.
+ */
+ if ( ( cache_ptr != NULL ) &&
+ ( H5C2_get_trace_file_ptr(cache_ptr, &trace_file_ptr) >= 0 ) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ sprintf(trace, "H5AC2_expunge_entry 0x%lx %d",
+ (unsigned long)addr,
+ (int)(type->id));
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ result = H5C2_expunge_entry(cache_ptr,
+ dxpl_id,
+ type,
+ addr);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, \
+ "H5C2_expunge_entry() failed.")
+ }
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d\n", trace, (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_expunge_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_flush
+ *
+ * Purpose: Flush (and possibly destroy) the metadata cache associated
+ * with the specified file.
+ *
+ * This is a re-write of an earlier version of the function
+ * which was reputedly capable of flushing (and destroying
+ * if requested) individual entries, individual entries if
+ * they match the supplied type, all entries of a given type,
+ * as well as all entries in the cache.
+ *
+ * As only this last capability is actually used at present,
+ * I have not implemented the other capabilities in this
+ * version of the function.
+ *
+ * The type and addr parameters are retained to avoid source
+ * code changed, but values other than NULL and HADDR_UNDEF
+ * respectively are errors. If all goes well, they should
+ * be removed, and the function renamed to something more
+ * descriptive -- perhaps H5AC2_flush_cache.
+ *
+ * If the cache contains protected entries, the function will
+ * fail, as protected entries cannot be flushed. However
+ * all unprotected entries should be flushed before the
+ * function returns failure.
+ *
+ * For historical purposes, the original version of the
+ * purpose section is reproduced below:
+ *
+ * ============ Original Version of "Purpose:" ============
+ *
+ * Flushes (and destroys if DESTROY is non-zero) the specified
+ * entry from the cache. If the entry TYPE is CACHE_FREE and
+ * ADDR is HADDR_UNDEF then all types of entries are
+ * flushed. If TYPE is CACHE_FREE and ADDR is defined then
+ * whatever is cached at ADDR is flushed. Otherwise the thing
+ * at ADDR is flushed if it is the correct type.
+ *
+ * If there are protected objects they will not be flushed.
+ * However, an attempt will be made to flush all non-protected
+ * items before this function returns failure.
+ *
+ * Return: Non-negative on success/Negative on failure if there was a
+ * request to flush all items and something was protected.
+ *
+ * Programmer: Robb Matzke
+ * matzke@llnl.gov
+ * Jul 9 1997
+ *
+ * Modifications:
+ * Robb Matzke, 1999-07-27
+ * The ADDR argument is passed by value.
+ *
+ * Complete re-write. See above for details. -- JRM 5/11/04
+ *
+ * Abstracted the guts of the function to H5C_flush_cache()
+ * in H5C.c, and then re-wrote the function as a wrapper for
+ * H5C_flush_cache().
+ *
+ * JRM - 6/7/04
+ *
+ * JRM - 7/5/05
+ * Modified function as part of a fix for a cache coherency
+ * bug in PHDF5. See the header comments on the H5AC2_aux_t
+ * structure for details.
+ *
+ * JRM -- 5/11/06
+ * Added call to the write_done callback.
+ *
+ * JRM -- 6/6/06
+ * Added trace file support.
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_flush(H5F_t *f, hid_t dxpl_id, unsigned flags)
+{
+ herr_t status;
+ herr_t ret_value = SUCCEED; /* Return value */
+#ifdef H5_HAVE_PARALLEL
+ H5AC2_aux_t * aux_ptr = NULL;
+ int mpi_code;
+#endif /* H5_HAVE_PARALLEL */
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+
+ FUNC_ENTER_NOAPI(H5AC2_flush, FAIL)
+
+ HDassert(f);
+ HDassert(f->shared->cache2);
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the flush, only the flags are really necessary in the trace file.
+ * Write the result to catch occult errors.
+ */
+ if ( ( f != NULL ) &&
+ ( f->shared != NULL ) &&
+ ( f->shared->cache2 != NULL ) &&
+ ( H5C2_get_trace_file_ptr(f->shared->cache2, &trace_file_ptr) >= 0) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ sprintf(trace, "H5AC2_flush 0x%x", flags);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+#ifdef H5_HAVE_PARALLEL
+ aux_ptr = f->shared->cache2->aux_ptr;
+
+ if ( aux_ptr != NULL ) {
+
+#if H5AC2_DEBUG_DIRTY_BYTES_CREATION
+ HDfprintf(stdout,
+ "%d::H5AC2_flush: (u/uu/i/iu/r/ru) = %d/%d/%d/%d/%d/%d\n",
+ (int)(aux_ptr->mpi_rank),
+ (int)(aux_ptr->unprotect_dirty_bytes),
+ (int)(aux_ptr->unprotect_dirty_bytes_updates),
+ (int)(aux_ptr->insert_dirty_bytes),
+ (int)(aux_ptr->insert_dirty_bytes_updates),
+ (int)(aux_ptr->rename_dirty_bytes),
+ (int)(aux_ptr->rename_dirty_bytes_updates));
+#endif /* H5AC2_DEBUG_DIRTY_BYTES_CREATION */
+
+ /* to prevent "messages from the future" we must synchronize all
+ * processes before we start the flush. Hence the following
+ * barrier.
+ */
+ if ( MPI_SUCCESS != (mpi_code = MPI_Barrier(aux_ptr->mpi_comm)) ) {
+
+ HMPI_GOTO_ERROR(FAIL, "MPI_Barrier failed", mpi_code)
+ }
+
+ /* if the clear only flag is set, this flush will not involve any
+ * disk I/O. In such cases, it is not necessary to let process 0
+ * flush first.
+ */
+ if ( ( aux_ptr->mpi_rank == 0 ) &&
+ ( (flags & H5AC2__FLUSH_CLEAR_ONLY_FLAG) == 0 ) ) {
+
+ unsigned init_flush_flags = H5AC2__NO_FLAGS_SET;
+
+ if ( ( (flags & H5AC2__FLUSH_MARKED_ENTRIES_FLAG) != 0 ) &&
+ ( (flags & H5AC2__FLUSH_INVALIDATE_FLAG) == 0 ) ) {
+
+ init_flush_flags |= H5AC2__FLUSH_MARKED_ENTRIES_FLAG;
+ }
+
+ aux_ptr->write_permitted = TRUE;
+
+ status = H5C2_flush_cache(f->shared->cache2,
+ dxpl_id,
+ init_flush_flags);
+
+ aux_ptr->write_permitted = FALSE;
+
+ if ( status < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't flush.")
+ }
+
+ if ( aux_ptr->write_done != NULL ) {
+
+ (aux_ptr->write_done)();
+ }
+
+ } /* end if ( aux_ptr->mpi_rank == 0 ) */
+
+ status = H5AC2_propagate_flushed_and_still_clean_entries_list(f,
+ H5AC2_noblock_dxpl_id,
+ f->shared->cache2,
+ FALSE);
+ } /* end if ( aux_ptr != NULL ) */
+#endif /* H5_HAVE_PARALLEL */
+
+ status = H5C2_flush_cache(f->shared->cache2,
+ dxpl_id,
+ flags);
+
+ if ( status < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't flush entry.")
+ }
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d\n", trace, (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_flush() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_get_entry_status
+ *
+ * Purpose: Given a file address, determine whether the metadata
+ * cache contains an entry at that location. If it does,
+ * also determine whether the entry is dirty, protected,
+ * pinned, etc. and return that information to the caller
+ * in *status_ptr.
+ *
+ * If the specified entry doesn't exist, set *status_ptr
+ * to zero.
+ *
+ * On error, the value of *status_ptr is undefined.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 4/27/06
+ *
+ * Modifications:
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_get_entry_status(H5F_t * f,
+ haddr_t addr,
+ unsigned * status_ptr)
+{
+ H5C2_t *cache_ptr = f->shared->cache2;
+ herr_t ret_value = SUCCEED; /* Return value */
+ herr_t result;
+ hbool_t in_cache;
+ hbool_t is_dirty;
+ hbool_t is_protected;
+ hbool_t is_pinned;
+ size_t entry_size;
+ unsigned status = 0;
+
+ FUNC_ENTER_NOAPI(H5AC2_get_entry_status, FAIL)
+
+ if ( ( cache_ptr == NULL ) ||
+ ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ||
+ ( ! H5F_addr_defined(addr) ) ||
+ ( status_ptr == NULL ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad param(s) on entry.")
+ }
+
+ result = H5C2_get_entry_status(cache_ptr, addr, &entry_size, &in_cache,
+ &is_dirty, &is_protected, &is_pinned);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_get_entry_status() failed.")
+ }
+
+ if ( in_cache ) {
+
+ status |= H5AC2_ES__IN_CACHE;
+
+ if ( is_dirty )
+ status |= H5AC2_ES__IS_DIRTY;
+
+ if ( is_protected )
+ status |= H5AC2_ES__IS_PROTECTED;
+
+ if ( is_pinned )
+ status |= H5AC2_ES__IS_PINNED;
+ }
+
+ *status_ptr = status;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_get_entry_status() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_set
+ *
+ * Purpose: Adds the specified thing to the cache. The thing need not
+ * exist on disk yet, but it must have an address and disk
+ * space reserved.
+ *
+ * If H5AC2_DEBUG is defined then this function checks
+ * that the object being inserted isn't a protected object.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Robb Matzke
+ * matzke@llnl.gov
+ * Jul 9 1997
+ *
+ * Modifications:
+ * Robb Matzke, 1999-07-27
+ * The ADDR argument is passed by value.
+ *
+ * Bill Wendling, 2003-09-16
+ * Added automatic "flush" if the FPHDF5 driver is being
+ * used. This'll write the metadata to the SAP where other,
+ * lesser processes can grab it.
+ *
+ * JRM - 5/13/04
+ * Complete re-write for the new metadata cache. The new
+ * code is functionally almost identical to the old, although
+ * the sanity check for a protected entry is now an assert
+ * at the beginning of the function.
+ *
+ * JRM - 6/7/04
+ * Abstracted the guts of the function to H5C_insert_entry()
+ * in H5C.c, and then re-wrote the function as a wrapper for
+ * H5C_insert_entry().
+ *
+ * JRM - 1/6/05
+ * Added the flags parameter. At present, this parameter is
+ * only used to set the new flush_marker field on the new
+ * entry. Since this doesn't apply to the SAP code, no change
+ * is needed there. Thus the only change to the body of the
+ * code is to pass the flags parameter through to
+ * H5C_insert_entry().
+ *
+ * JRM - 6/6/05
+ * Added code to force newly inserted entries to be dirty
+ * in the flexible parallel case. The normal case is handled
+ * in H5C.c. This is part of a series of changes directed at
+ * moving management of the dirty flag on cache entries into
+ * the cache code.
+ *
+ * JRM - 7/5/05
+ * Added code to track dirty byte generation, and to trigger
+ * clean entry list propagation when it exceeds a user
+ * specified threshold. Note that this code only applies in
+ * the PHDF5 case. It should have no effect on either the
+ * serial or FPHSD5 cases.
+ *
+ * JRM - 6/6/06
+ * Added trace file support.
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_set(H5F_t *f, hid_t dxpl_id, const H5AC2_class_t *type, haddr_t addr, size_t len, void *thing, unsigned int flags)
+{
+ herr_t result;
+ H5AC2_info_t *info;
+ H5AC2_t *cache;
+ herr_t ret_value=SUCCEED; /* Return value */
+#ifdef H5_HAVE_PARALLEL
+ H5AC2_aux_t * aux_ptr = NULL;
+#endif /* H5_HAVE_PARALLEL */
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ size_t trace_entry_size = 0;
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_set, FAIL)
+
+ HDassert(f);
+ HDassert(f->shared->cache2);
+ HDassert(type);
+ HDassert(type->serialize);
+ HDassert(H5F_addr_defined(addr));
+ HDassert(thing);
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the insert, only the addr, size, type id and flags are really
+ * necessary in the trace file. Write the result to catch occult
+ * errors.
+ *
+ * Note that some data is not available right now -- put what we can
+ * in the trace buffer now, and fill in the rest at the end.
+ */
+ if ( ( f != NULL ) &&
+ ( f->shared != NULL ) &&
+ ( f->shared->cache2 != NULL ) &&
+ ( H5C2_get_trace_file_ptr(f->shared->cache2, &trace_file_ptr) >= 0) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ sprintf(trace, "H5AC2_set 0x%lx %ld %d 0x%x",
+ (unsigned long)addr,
+ (long)len,
+ type->id,
+ flags);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ /* Get local copy of this information */
+ cache = f->shared->cache2;
+ info = (H5AC2_info_t *)thing;
+
+ info->addr = addr;
+ info->type = type;
+ info->is_protected = FALSE;
+
+#ifdef H5_HAVE_PARALLEL
+ if ( NULL != (aux_ptr = f->shared->cache2->aux_ptr) ) {
+
+ result = H5AC2_log_inserted_entry(f,
+ f->shared->cache2,
+ (H5AC2_info_t *)thing,
+ type,
+ addr,
+ len);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \
+ "H5AC2_log_inserted_entry() failed.")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ result = H5C2_insert_entry(cache,
+ dxpl_id,
+ type,
+ addr,
+ len,
+ thing,
+ flags);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "H5C2_insert_entry() failed")
+ }
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ /* make note of the entry size */
+ trace_entry_size = ((H5C2_cache_entry_t *)thing)->size;
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+#ifdef H5_HAVE_PARALLEL
+ if ( ( aux_ptr != NULL ) &&
+ ( aux_ptr->dirty_bytes >= aux_ptr->dirty_bytes_threshold ) ) {
+
+ result = H5AC2_propagate_flushed_and_still_clean_entries_list(f,
+ H5AC2_noblock_dxpl_id,
+ f->shared->cache2,
+ TRUE);
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "Can't propagate clean entries list.")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d %d\n", trace,
+ (int)trace_entry_size,
+ (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_set() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_mark_pinned_entry_dirty
+ *
+ * Purpose: Mark a pinned entry as dirty. The target entry MUST be
+ * be pinned, and MUST be unprotected.
+ *
+ * If the entry has changed size, the function updates
+ * data structures for the size change.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 4/11/06
+ *
+ * Modifications:
+ *
+ * Added trace file support. JRM -- 6/6/06
+ *
+ * Modified code in support of revised cache API needed
+ * to permit journaling. JRM -- 10/18/07
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_mark_pinned_entry_dirty(H5F_t * f,
+ void * thing,
+ hbool_t size_changed,
+ size_t new_size)
+{
+ H5C2_t *cache_ptr = f->shared->cache2;
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_mark_pinned_entry_dirty, FAIL)
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the mark pinned entry dirty call, only the addr, size_changed,
+ * and new_size are really necessary in the trace file. Write the result
+ * to catch occult errors.
+ */
+ if ( ( f != NULL ) &&
+ ( f->shared != NULL ) &&
+ ( f->shared->cache2 != NULL ) &&
+ ( H5C2_get_trace_file_ptr(f->shared->cache2, &trace_file_ptr) >= 0) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ sprintf(trace, "H5AC2_mark_pinned_entry_dirty 0x%lx %d %d",
+ (unsigned long)(((H5C2_cache_entry_t *)thing)->addr),
+ (int)size_changed,
+ (int)new_size);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+#ifdef H5_HAVE_PARALLEL
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( thing );
+
+ if ( ( ((H5AC2_info_t *)thing)->is_dirty == FALSE ) &&
+ ( NULL != cache_ptr->aux_ptr) ) {
+
+ H5AC2_info_t * entry_ptr;
+
+ HDassert( ( size_changed == TRUE ) || ( size_changed == FALSE ) );
+
+ entry_ptr = (H5AC2_info_t *)thing;
+
+ if ( ! ( entry_ptr->is_pinned ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "Entry isn't pinned??")
+ }
+
+ if ( entry_ptr->is_protected ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "Entry is protected??")
+ }
+
+ result = H5AC2_log_dirtied_entry(cache_ptr,
+ entry_ptr,
+ entry_ptr->addr,
+ size_changed,
+ new_size);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "H5AC2_log_dirtied_entry() failed.")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ result = H5C2_mark_pinned_entry_dirty(cache_ptr,
+ thing,
+ size_changed,
+ new_size);
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "H5C2_mark_pinned_entry_dirty() failed.")
+
+ }
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d\n", trace, (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_mark_pinned_entry_dirty() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_mark_pinned_or_protected_entry_dirty
+ *
+ * Purpose: Mark a pinned or protected entry as dirty. The target
+ * entry MUST be either pinned, protected, or both.
+ *
+ * Unlike H5AC2_mark_pinned_entry_dirty(), this function does
+ * not support size changes.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 5/16/06
+ *
+ * Modifications:
+ *
+ * Added trace file support. JRM -- 6/6/06
+ *
+ * Modified code in support of revised cache API needed
+ * to permit journaling. JRM -- 10/18/07
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_mark_pinned_or_protected_entry_dirty(H5F_t * f,
+ void * thing)
+{
+ H5C2_t * cache_ptr = f->shared->cache2;
+#ifdef H5_HAVE_PARALLEL
+ H5AC2_info_t * info_ptr;
+#endif /* H5_HAVE_PARALLEL */
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_mark_pinned_or_protected_entry_dirty, FAIL)
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the mark pinned or protected entry dirty call, only the addr
+ * is really necessary in the trace file. Write the result to catch
+ * occult errors.
+ */
+ if ( ( f != NULL ) &&
+ ( f->shared != NULL ) &&
+ ( f->shared->cache2 != NULL ) &&
+ ( H5C2_get_trace_file_ptr(f->shared->cache2, &trace_file_ptr) >= 0) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ sprintf(trace, "H5AC2_mark_pinned_or_protected_entry_dirty 0x%lx",
+ (unsigned long)(((H5C2_cache_entry_t *)thing)->addr));
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+#ifdef H5_HAVE_PARALLEL
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( thing );
+
+ info_ptr = (H5AC2_info_t *)thing;
+
+ if ( ( info_ptr->is_dirty == FALSE ) &&
+ ( ! ( info_ptr->is_protected ) ) &&
+ ( info_ptr->is_pinned ) &&
+ ( NULL != cache_ptr->aux_ptr) ) {
+
+ result = H5AC2_log_dirtied_entry(cache_ptr,
+ info_ptr,
+ info_ptr->addr,
+ FALSE,
+ 0);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "H5AC2_log_dirtied_entry() failed.")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ result = H5C2_mark_pinned_or_protected_entry_dirty(cache_ptr, thing);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "H5C2_mark_pinned_entry_dirty() failed.")
+
+ }
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d\n", trace, (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_mark_pinned_entry_dirty() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_rename
+ *
+ * Purpose: Use this function to notify the cache that an object's
+ * file address changed.
+ *
+ * If H5AC2_DEBUG is defined then this function checks
+ * that the old and new addresses don't correspond to the
+ * address of a protected object.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Robb Matzke
+ * matzke@llnl.gov
+ * Jul 9 1997
+ *
+ * Modifications:
+ * Robb Matzke, 1999-07-27
+ * The OLD_ADDR and NEW_ADDR arguments are passed by value.
+ *
+ * JRM 5/17/04
+ * Complete rewrite for the new meta-data cache.
+ *
+ * JRM - 6/7/04
+ * Abstracted the guts of the function to H5C_rename_entry()
+ * in H5C.c, and then re-wrote the function as a wrapper for
+ * H5C_rename_entry().
+ *
+ * JRM - 7/5/05
+ * Added code to track dirty byte generation, and to trigger
+ * clean entry list propagation when it exceeds a user
+ * specified threshold. Note that this code only applies in
+ * the PHDF5 case. It should have no effect on either the
+ * serial or FPHSD5 cases.
+ *
+ * Note that this code presumes that the renamed entry will
+ * be present in all caches -- which it must be at present.
+ * To maintain this invarient, only rename entries immediately
+ * after you unprotect them.
+ *
+ * JRM - 6/6/06
+ * Added trace file support.
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_rename(H5F_t *f, const H5AC2_class_t *type, haddr_t old_addr, haddr_t new_addr)
+{
+ herr_t result;
+ herr_t ret_value=SUCCEED; /* Return value */
+#ifdef H5_HAVE_PARALLEL
+ H5AC2_aux_t * aux_ptr = NULL;
+#endif /* H5_HAVE_PARALLEL */
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_rename, FAIL)
+
+ HDassert(f);
+ HDassert(f->shared->cache2);
+ HDassert(type);
+ HDassert(H5F_addr_defined(old_addr));
+ HDassert(H5F_addr_defined(new_addr));
+ HDassert(H5F_addr_ne(old_addr, new_addr));
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the rename call, only the old addr and new addr are really
+ * necessary in the trace file. Include the type id so we don't have to
+ * look it up. Also write the result to catch occult errors.
+ */
+ if ( ( f != NULL ) &&
+ ( f->shared != NULL ) &&
+ ( f->shared->cache2 != NULL ) &&
+ ( H5C2_get_trace_file_ptr(f->shared->cache2, &trace_file_ptr) >= 0) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ sprintf(trace, "H5AC2_rename 0x%lx 0x%lx %d",
+ (unsigned long)old_addr,
+ (unsigned long)new_addr,
+ (int)(type->id));
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+#ifdef H5_HAVE_PARALLEL
+ if ( NULL != (aux_ptr = f->shared->cache2->aux_ptr) ) {
+
+ result = H5AC2_log_renamed_entry(f->shared->cache2,
+ old_addr,
+ new_addr);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "H5AC2_log_renamed_entry() failed.")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ result = H5C2_rename_entry(f->shared->cache2,
+ type,
+ old_addr,
+ new_addr);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \
+ "H5C2_rename_entry() failed.")
+ }
+
+#ifdef H5_HAVE_PARALLEL
+ if ( ( aux_ptr != NULL ) &&
+ ( aux_ptr->dirty_bytes >= aux_ptr->dirty_bytes_threshold ) ) {
+
+ result = H5AC2_propagate_flushed_and_still_clean_entries_list(f,
+ H5AC2_noblock_dxpl_id,
+ f->shared->cache2,
+ TRUE);
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "Can't propagate clean entries list.")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d\n", trace, (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_rename() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_pin_protected_entry()
+ *
+ * Purpose: Pin a protected cache entry. The entry must be protected
+ * at the time of call, and must be unpinned.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 4/27/06
+ *
+ * Modifications:
+ *
+ * Added trace file support. 6/6/06
+ *
+ * Modified code in support of revised cache API needed
+ * to permit journaling. JRM - 10/18/07
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_pin_protected_entry(H5F_t * f,
+ void * thing)
+{
+ H5C2_t *cache_ptr = f->shared->cache2;
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_pin_protected_entry, FAIL)
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the pin protected entry call, only the addr is really necessary
+ * in the trace file. Also write the result to catch occult errors.
+ */
+ if ( ( f != NULL ) &&
+ ( f->shared != NULL ) &&
+ ( f->shared->cache2 != NULL ) &&
+ ( H5C2_get_trace_file_ptr(f->shared->cache2, &trace_file_ptr) >= 0) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ sprintf(trace, "H5AC2_pin_protected_entry 0x%lx",
+ (unsigned long)(((H5C2_cache_entry_t *)thing)->addr));
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ result = H5C2_pin_protected_entry(cache_ptr, thing);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, \
+ "H5C2_pin_protected_entry() failed.")
+ }
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d\n", trace, (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_pin_protected_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_protect
+ *
+ * Purpose: If the target entry is not in the cache, load it. If
+ * necessary, attempt to evict one or more entries to keep
+ * the cache within its maximum size.
+ *
+ * Mark the target entry as protected, and return its address
+ * to the caller. The caller must call H5AC2_unprotect() when
+ * finished with the entry.
+ *
+ * While it is protected, the entry may not be either evicted
+ * or flushed -- nor may it be accessed by another call to
+ * H5AC2_protect. Any attempt to do so will result in a failure.
+ *
+ * This comment is a re-write of the original Purpose: section.
+ * For historical interest, the original version is reproduced
+ * below:
+ *
+ * Original Purpose section:
+ *
+ * Similar to H5AC2_find() except the object is removed from
+ * the cache and given to the caller, preventing other parts
+ * of the program from modifying the protected object or
+ * preempting it from the cache.
+ *
+ * The caller must call H5AC2_unprotect() when finished with
+ * the pointer.
+ *
+ * If H5AC2_DEBUG is defined then we check that the
+ * requested object isn't already protected.
+ *
+ * Return: Success: Ptr to the object.
+ *
+ * Failure: NULL
+ *
+ * Programmer: Robb Matzke
+ * matzke@llnl.gov
+ * Sep 2 1997
+ *
+ * Modifications:
+ * Robb Matzke, 1999-07-27
+ * The ADDR argument is passed by value.
+ *
+ * Bill Wendling, 2003-09-10
+ * Added parameter to indicate whether this is a READ or
+ * WRITE type of protect.
+ *
+ * JRM -- 5/17/04
+ * Complete re-write for the new client cache. See revised
+ * Purpose section above.
+ *
+ * JRM - 6/7/04
+ * Abstracted the guts of the function to H5C2_protect()
+ * in H5C2.c, and then re-wrote the function as a wrapper for
+ * H5C2_protect().
+ *
+ * JRM - 6/6/06
+ * Added trace file support.
+ *
+ * JRM - 3/18/07
+ * Modified code to support the new flags parameter for
+ * H5C2_protect(). For now, that means passing in the
+ * H5C2_READ_ONLY_FLAG if rw == H5AC2_READ.
+ *
+ * Also updated the trace file output to save the
+ * rw parameter, since we are now doing something with it.
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+void *
+H5AC2_protect(H5F_t *f,
+ hid_t dxpl_id,
+ const H5AC2_class_t *type,
+ haddr_t addr,
+ size_t len,
+ const void *udata,
+ H5AC2_protect_t rw)
+{
+ /* char * fcn_name = "H5AC2_protect"; */
+ unsigned protect_flags = H5C2__NO_FLAGS_SET;
+ void * thing = (void *)NULL;
+ void * ret_value; /* Return value */
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ size_t trace_entry_size = 0;
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_protect, NULL)
+
+ /* check args */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(f->shared->cache2);
+ HDassert(type);
+ HDassert(type->serialize);
+ HDassert(H5F_addr_defined(addr));
+
+ /* Check for invalid access request */
+ if(0 == (f->intent & H5F_ACC_RDWR) && rw == H5AC2_WRITE)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "no write intent on file")
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the protect call, only the addr and type id is really necessary
+ * in the trace file. Include the size of the entry protected as a
+ * sanity check. Also indicate whether the call was successful to
+ * catch occult errors.
+ */
+ if ( ( f != NULL ) &&
+ ( f->shared != NULL ) &&
+ ( f->shared->cache2 != NULL ) &&
+ ( H5C2_get_trace_file_ptr(f->shared->cache2, &trace_file_ptr) >= 0) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ char * rw_string;
+
+ if ( rw == H5AC2_WRITE ) {
+
+ rw_string = "H5AC2_WRITE";
+
+ } else if ( rw == H5AC2_READ ) {
+
+ rw_string = "H5AC2_READ";
+
+ } else {
+
+ rw_string = "???";
+ }
+
+ sprintf(trace, "H5AC2_protect 0x%lx %ld %d %s",
+ (unsigned long)addr,
+ (long)len,
+ (int)(type->id),
+ rw_string);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ if ( rw == H5AC2_READ ) {
+
+ protect_flags |= H5C2__READ_ONLY_FLAG;
+ }
+
+ thing = H5C2_protect(f->shared->cache2,
+ dxpl_id,
+ type,
+ addr,
+ len,
+ udata,
+ protect_flags);
+
+ if ( thing == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C2_protect() failed.")
+ }
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ /* make note of the entry size */
+ trace_entry_size = ((H5C2_cache_entry_t *)thing)->size;
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ /* Set return value */
+ ret_value = thing;
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d %d\n", trace,
+ (int)trace_entry_size,
+ (int)(ret_value != NULL));
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_protect() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_resize_pinned_entry
+ *
+ * Purpose: Resize a pinned entry. The target entry MUST be
+ * be pinned, and MUST not be unprotected.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 7/5/06
+ *
+ * Modifications:
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_resize_pinned_entry(H5F_t * f,
+ void * thing,
+ size_t new_size)
+{
+ H5C2_t *cache_ptr = f->shared->cache2;
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_resize_pinned_entry, FAIL)
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the resize pinned entry call, only the addr, and new_size are
+ * really necessary in the trace file. Write the result to catch
+ * occult errors.
+ */
+ if ( ( f != NULL ) &&
+ ( f->shared != NULL ) &&
+ ( f->shared->cache2 != NULL ) &&
+ ( H5C2_get_trace_file_ptr(f->shared->cache2, &trace_file_ptr) >= 0) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ sprintf(trace, "H5AC2_resize_pinned_entry 0x%lx %d",
+ (unsigned long)(((H5C2_cache_entry_t *)thing)->addr),
+ (int)new_size);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+#ifdef H5_HAVE_PARALLEL
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( thing );
+
+ if ( ( ((H5AC2_info_t *)thing)->is_dirty == FALSE ) &&
+ ( NULL != cache_ptr->aux_ptr) ) {
+
+ H5AC2_info_t * entry_ptr;
+
+ entry_ptr = (H5AC2_info_t *)thing;
+
+ if ( ! ( entry_ptr->is_pinned ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTRESIZE, FAIL, \
+ "Entry isn't pinned??")
+ }
+
+ if ( entry_ptr->is_protected ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTRESIZE, FAIL, \
+ "Entry is protected??")
+ }
+
+ result = H5AC2_log_dirtied_entry(cache_ptr,
+ entry_ptr,
+ entry_ptr->addr,
+ TRUE,
+ new_size);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "H5AC2_log_dirtied_entry() failed.")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ result = H5C2_resize_pinned_entry(cache_ptr,
+ thing,
+ new_size);
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTRESIZE, FAIL, \
+ "H5C2_resize_pinned_entry() failed.")
+
+ }
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d\n", trace, (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_resize_pinned_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_unpin_entry()
+ *
+ * Purpose: Unpin a cache entry. The entry must be unprotected at
+ * the time of call, and must be pinned.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 4/11/06
+ *
+ * Modifications:
+ *
+ * Added code supporting the trace file. JRM -- 6/7/06
+ *
+ * Modified code in support of revised cache API needed
+ * to permit journaling. JRM - 10/18/07
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_unpin_entry(H5F_t * f,
+ void * thing)
+{
+ H5C2_t *cache_ptr = f->shared->cache2;
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_unpin_entry, FAIL)
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the unpin entry call, only the addr is really necessary
+ * in the trace file. Also write the result to catch occult errors.
+ */
+ if ( ( f != NULL ) &&
+ ( f->shared != NULL ) &&
+ ( f->shared->cache2 != NULL ) &&
+ ( H5C2_get_trace_file_ptr(f->shared->cache2, &trace_file_ptr) >= 0) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ sprintf(trace, "H5AC2_unpin_entry 0x%lx",
+ (unsigned long)(((H5C2_cache_entry_t *)thing)->addr));
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ result = H5C2_unpin_entry(cache_ptr, thing);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "H5C2_unpin_entry() failed.")
+ }
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d\n", trace, (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_unpin_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_unprotect
+ *
+ * Purpose: Undo an H5AC2_protect() call -- specifically, mark the
+ * entry as unprotected, remove it from the protected list,
+ * and give it back to the replacement policy.
+ *
+ * The TYPE and ADDR arguments must be the same as those in
+ * the corresponding call to H5AC2_protect() and the THING
+ * argument must be the value returned by that call to
+ * H5AC2_protect().
+ *
+ * If the deleted flag is TRUE, simply remove the target entry
+ * from the cache, clear it, and free it without writing it to
+ * disk.
+ *
+ * This verion of the function is a complete re-write to
+ * use the new metadata cache. While there isn't all that
+ * much difference between the old and new Purpose sections,
+ * the original version is given below.
+ *
+ * Original purpose section:
+ *
+ * This function should be called to undo the effect of
+ * H5AC2_protect(). The TYPE and ADDR arguments should be the
+ * same as the corresponding call to H5AC2_protect() and the
+ * THING argument should be the value returned by H5AC2_protect().
+ * If the DELETED flag is set, then this object has been deleted
+ * from the file and should not be returned to the cache.
+ *
+ * If H5AC2_DEBUG is defined then this function fails
+ * if the TYPE and ADDR arguments are not what was used when the
+ * object was protected or if the object was never protected.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Robb Matzke
+ * matzke@llnl.gov
+ * Sep 2 1997
+ *
+ * Modifications:
+ * Robb Matzke, 1999-07-27
+ * The ADDR argument is passed by value.
+ *
+ * Quincey Koziol, 2003-03-19
+ * Added "deleted" argument
+ *
+ * Bill Wendling, 2003-09-18
+ * If this is an FPHDF5 driver and the data is dirty,
+ * perform a "flush" that writes the data to the SAP.
+ *
+ * John Mainzer 5/19/04
+ * Complete re-write for the new metadata cache.
+ *
+ * JRM - 6/7/04
+ * Abstracted the guts of the function to H5C_unprotect()
+ * in H5C.c, and then re-wrote the function as a wrapper for
+ * H5C_unprotect().
+ *
+ * JRM - 1/6/05
+ * Replaced the deleted parameter with the new flags parameter.
+ * Since the deleted parameter is not used by the FPHDF5 code,
+ * the only change in the body is to replace the deleted
+ * parameter with the flags parameter in the call to
+ * H5C_unprotect().
+ *
+ * JRM - 6/6/05
+ * Added the dirtied flag and supporting code. This is
+ * part of a collection of changes directed at moving
+ * management of cache entry dirty flags into the H5C code.
+ *
+ * JRM - 7/5/05
+ * Added code to track dirty byte generation, and to trigger
+ * clean entry list propagation when it exceeds a user
+ * specified threshold. Note that this code only applies in
+ * the PHDF5 case. It should have no effect on either the
+ * serial or FPHSD5 cases.
+ *
+ * JRM - 9/8/05
+ * Added code to track entry size changes. This is necessary
+ * as it can effect dirty byte creation counts, thereby
+ * throwing the caches out of sync in the PHDF5 case.
+ *
+ * JRM - 5/16/06
+ * Added code to use the new dirtied field in
+ * H5C_cache_entry_t in the test to see if the entry has
+ * been dirtied.
+ *
+ * JRM - 6/7/06
+ * Added support for the trace file.
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ * Note that the H5AC2__SIZE_CHANGED_FLAG must now be set if
+ * the size of the entry has changed.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_unprotect(H5F_t *f, hid_t dxpl_id, const H5AC2_class_t *type,
+ haddr_t addr, size_t new_size, void *thing, unsigned flags)
+{
+ herr_t result;
+ herr_t ret_value=SUCCEED; /* Return value */
+ hbool_t size_changed = FALSE;
+ hbool_t dirtied;
+#ifdef H5_HAVE_PARALLEL
+ H5AC2_aux_t * aux_ptr = NULL;
+#endif /* H5_HAVE_PARALLEL */
+#if H5AC2__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ unsigned trace_flags = 0;
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_unprotect, FAIL)
+
+ HDassert(f);
+ HDassert(f->shared->cache2);
+ HDassert(type);
+ HDassert(type->deserialize);
+ HDassert(H5F_addr_defined(addr));
+ HDassert(thing);
+ HDassert( ((H5AC2_info_t *)thing)->addr == addr );
+ HDassert( ((H5AC2_info_t *)thing)->type == type );
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the unprotect call, only the addr, type id, flags, and possible
+ * new size are really necessary in the trace file. Write the return
+ * value to catch occult errors.
+ */
+ if ( ( f != NULL ) &&
+ ( f->shared != NULL ) &&
+ ( f->shared->cache2 != NULL ) &&
+ ( H5C2_get_trace_file_ptr(f->shared->cache2, &trace_file_ptr) >= 0) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ sprintf(trace, "H5AC2_unprotect 0x%lx %d",
+ (unsigned long)addr,
+ (int)(type->id));
+
+ trace_flags = flags;
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ dirtied = ( ( (flags & H5AC2__DIRTIED_FLAG) == H5AC2__DIRTIED_FLAG ) ||
+ ( ((H5AC2_info_t *)thing)->dirtied ) );
+
+ size_changed = ( (flags & H5AC2__SIZE_CHANGED_FLAG) ==
+ H5AC2__SIZE_CHANGED_FLAG );
+
+#ifdef H5_HAVE_PARALLEL
+ if ( ( dirtied ) && ( ((H5AC2_info_t *)thing)->is_dirty == FALSE ) &&
+ ( NULL != (aux_ptr = f->shared->cache2->aux_ptr) ) ) {
+
+ result = H5AC2_log_dirtied_entry(f->shared->cache2,
+ (H5AC2_info_t *)thing,
+ addr,
+ size_changed,
+ new_size);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "H5AC2_log_dirtied_entry() failed.")
+ }
+ }
+
+ if ( ( (flags & H5C2__DELETED_FLAG) != 0 ) &&
+ ( NULL != (aux_ptr = f->shared->cache2->aux_ptr) ) &&
+ ( aux_ptr->mpi_rank == 0 ) ) {
+
+ result = H5AC2_log_deleted_entry(f->shared->cache2,
+ (H5AC2_info_t *)thing,
+ addr,
+ flags);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "H5AC2_log_deleted_entry() failed.")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ result = H5C2_unprotect(f->shared->cache2,
+ dxpl_id,
+ type,
+ addr,
+ thing,
+ flags,
+ new_size);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "H5C2_unprotect() failed.")
+ }
+
+#ifdef H5_HAVE_PARALLEL
+ if ( ( aux_ptr != NULL ) &&
+ ( aux_ptr->dirty_bytes >= aux_ptr->dirty_bytes_threshold ) ) {
+
+ result = H5AC2_propagate_flushed_and_still_clean_entries_list(f,
+ H5AC2_noblock_dxpl_id,
+ f->shared->cache2,
+ TRUE);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "Can't propagate clean entries list.")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ if ( trace_file_ptr != NULL ) {
+
+ HDfprintf(trace_file_ptr, "%s %d %x %d\n",
+ trace,
+ (int)new_size,
+ (unsigned)flags,
+ (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_unprotect() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: HA5C_set_write_done_callback
+ *
+ * Purpose: Set the value of the write_done callback. This callback
+ * is used to improve performance of the parallel test bed
+ * for the cache.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 5/11/06
+ *
+ * Modifications:
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+herr_t
+H5AC2_set_write_done_callback(H5C2_t * cache_ptr,
+ void (* write_done)(void))
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5AC2_aux_t * aux_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5AC2_set_write_done_callback, FAIL)
+
+ /* This would normally be an assert, but we need to use an HGOTO_ERROR
+ * call to shut up the compiler.
+ */
+ if ( ( ! cache_ptr ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr")
+ }
+
+ aux_ptr = cache_ptr->aux_ptr;
+
+ HDassert( aux_ptr != NULL );
+ HDassert( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+
+ aux_ptr->write_done = write_done;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_set_write_done_callback() */
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_stats
+ *
+ * Purpose: Prints statistics about the cache.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Robb Matzke
+ * Thursday, October 30, 1997
+ *
+ * Modifications:
+ * John Mainzer 5/19/04
+ * Re-write to support the new metadata cache.
+ *
+ * JRM - 6/7/04
+ * Abstracted the guts of the function to H5C_stats()
+ * in H5C.c, and then re-wrote the function as a wrapper for
+ * H5C_stats().
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC2_stats(const H5F_t *f)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5AC2_stats, FAIL)
+
+ HDassert(f);
+ HDassert(f->shared->cache2);
+
+ /* at present, this can't fail */
+ (void)H5C2_stats(f->shared->cache2, f->name, FALSE);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_stats() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_get_cache_auto_resize_config
+ *
+ * Purpose: Wrapper function for H5C2_get_cache_auto_resize_config().
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 3/10/05
+ *
+ * Modifications:
+ *
+ * JRM - 4/6/05
+ * Reworked for the addition of struct H5AC2_cache_config_t.
+ *
+ * JRM - 10/25/05
+ * Added support for the new dirty_bytes_threshold field of
+ * both H5AC2_cache_config_t and H5AC2_aux_t.
+ *
+ * JRM - 6/8/06
+ * Added support for the new trace file related fields.
+ *
+ * JRM - 7/28/07
+ * Added support for the new evictions enabled related fields.
+ *
+ * Observe that H5AC2_get_cache_auto_resize_config() and
+ * H5AC2_set_cache_auto_resize_config() are becoming generic
+ * metadata cache configuration routines as they gain
+ * switches for functions that are only tenuously related
+ * to auto resize configuration.
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_get_cache_auto_resize_config(H5AC2_t * cache_ptr,
+ H5AC2_cache_config_t *config_ptr)
+{
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+ hbool_t evictions_enabled;
+ H5C2_auto_size_ctl_t internal_config;
+
+ FUNC_ENTER_NOAPI(H5AC2_get_cache_auto_resize_config, FAIL)
+
+ if ( ( cache_ptr == NULL )
+ ||
+#ifdef H5_HAVE_PARALLEL
+ ( ( cache_ptr->aux_ptr != NULL )
+ &&
+ ( ((H5AC2_aux_t *)(cache_ptr->aux_ptr))->magic
+ !=
+ H5AC2__H5AC2_AUX_T_MAGIC
+ )
+ )
+ ||
+#endif /* H5_HAVE_PARALLEL */
+ ( config_ptr == NULL )
+ ||
+ ( config_ptr->version != H5AC2__CURR_CACHE_CONFIG_VERSION )
+ )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Bad cache_ptr or config_ptr on entry.")
+
+ }
+
+ result = H5C2_get_cache_auto_resize_config((H5C2_t *)cache_ptr,
+ &internal_config);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_get_cache_auto_resize_config() failed.")
+ }
+
+ result = H5C2_get_evictions_enabled((H5C2_t *)cache_ptr,
+ &evictions_enabled);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_get_resize_enabled() failed.")
+ }
+
+ if ( internal_config.rpt_fcn == NULL ) {
+
+ config_ptr->rpt_fcn_enabled = FALSE;
+
+ } else {
+
+ config_ptr->rpt_fcn_enabled = TRUE;
+ }
+
+ config_ptr->open_trace_file = FALSE;
+ config_ptr->close_trace_file = FALSE;
+ config_ptr->trace_file_name[0] = '\0';
+ config_ptr->evictions_enabled = evictions_enabled;
+ config_ptr->set_initial_size = internal_config.set_initial_size;
+ config_ptr->initial_size = internal_config.initial_size;
+ config_ptr->min_clean_fraction = internal_config.min_clean_fraction;
+ config_ptr->max_size = internal_config.max_size;
+ config_ptr->min_size = internal_config.min_size;
+ config_ptr->epoch_length = (long)(internal_config.epoch_length);
+ config_ptr->incr_mode = internal_config.incr_mode;
+ config_ptr->lower_hr_threshold = internal_config.lower_hr_threshold;
+ config_ptr->increment = internal_config.increment;
+ config_ptr->apply_max_increment = internal_config.apply_max_increment;
+ config_ptr->max_increment = internal_config.max_increment;
+ config_ptr->decr_mode = internal_config.decr_mode;
+ config_ptr->upper_hr_threshold = internal_config.upper_hr_threshold;
+ config_ptr->decrement = internal_config.decrement;
+ config_ptr->apply_max_decrement = internal_config.apply_max_decrement;
+ config_ptr->max_decrement = internal_config.max_decrement;
+ config_ptr->epochs_before_eviction =
+ (int)(internal_config.epochs_before_eviction);
+ config_ptr->apply_empty_reserve = internal_config.apply_empty_reserve;
+ config_ptr->empty_reserve = internal_config.empty_reserve;
+
+#ifdef H5_HAVE_PARALLEL
+ if ( cache_ptr->aux_ptr != NULL ) {
+
+ config_ptr->dirty_bytes_threshold =
+ ((H5AC2_aux_t *)(cache_ptr->aux_ptr))->dirty_bytes_threshold;
+
+ } else {
+#endif /* H5_HAVE_PARALLEL */
+
+ config_ptr->dirty_bytes_threshold = H5AC2__DEFAULT_DIRTY_BYTES_THRESHOLD;
+
+#ifdef H5_HAVE_PARALLEL
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_get_cache_auto_resize_config() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_get_cache_size
+ *
+ * Purpose: Wrapper function for H5C2_get_cache_size().
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 3/11/05
+ *
+ * Modifications:
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_get_cache_size(H5AC2_t * cache_ptr,
+ size_t * max_size_ptr,
+ size_t * min_clean_size_ptr,
+ size_t * cur_size_ptr,
+ int32_t * cur_num_entries_ptr)
+{
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5AC2_get_cache_size, FAIL)
+
+ result = H5C2_get_cache_size((H5C2_t *)cache_ptr,
+ max_size_ptr,
+ min_clean_size_ptr,
+ cur_size_ptr,
+ cur_num_entries_ptr);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_get_cache_size() failed.")
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_get_cache_size() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_get_cache_hit_rate
+ *
+ * Purpose: Wrapper function for H5C2_get_cache_hit_rate().
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 3/10/05
+ *
+ * Modifications:
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_get_cache_hit_rate(H5AC2_t * cache_ptr,
+ double * hit_rate_ptr)
+
+{
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5AC2_get_cache_hit_rate, FAIL)
+
+ result = H5C2_get_cache_hit_rate((H5C2_t *)cache_ptr, hit_rate_ptr);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_get_cache_hit_rate() failed.")
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_get_cache_hit_rate() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5AC2_reset_cache_hit_rate_stats()
+ *
+ * Purpose: Wrapper function for H5C2_reset_cache_hit_rate_stats().
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer, 3/10/05
+ *
+ * Modifications:
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_reset_cache_hit_rate_stats(H5AC2_t * cache_ptr)
+{
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5AC2_reset_cache_hit_rate_stats, FAIL)
+
+ result = H5C2_reset_cache_hit_rate_stats((H5C2_t *)cache_ptr);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_reset_cache_hit_rate_stats() failed.")
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_reset_cache_hit_rate_stats() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_set_cache_auto_resize_config
+ *
+ * Purpose: Wrapper function for H5C2_set_cache_auto_resize_config().
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 3/10/05
+ *
+ * Modifications:
+ *
+ * John Mainzer -- 4/6/05
+ * Updated for the addition of H5AC2_cache_config_t.
+ *
+ * John Mainzer -- 10/25/05
+ * Added support for the new dirty_bytes_threshold field of
+ * both H5AC2_cache_config_t and H5AC2_aux_t.
+ *
+ * John Mainzer -- 6/7/06
+ * Added trace file support.
+ *
+ * John Mainzer -- 7/28/07
+ * Added support for the new evictions enabled related fields.
+ *
+ * Observe that H5AC2_get_cache_auto_resize_config() and
+ * H5AC2_set_cache_auto_resize_config() are becoming generic
+ * metadata cache configuration routines as they gain
+ * switches for functions that are only tenuously related
+ * to auto resize configuration.
+ *
+ * JRM - 10/18/07
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_set_cache_auto_resize_config(H5AC2_t * cache_ptr,
+ H5AC2_cache_config_t *config_ptr)
+{
+ /* const char * fcn_name = "H5AC2_set_cache_auto_resize_config"; */
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5C2_auto_size_ctl_t internal_config;
+#if H5AC2__TRACE_FILE_ENABLED
+ H5AC2_cache_config_t trace_config = H5AC2__DEFAULT_CACHE_CONFIG;
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_ENTER_NOAPI(H5AC2_set_cache_auto_resize_config, FAIL)
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* Make note of the new configuration. Don't look up the trace file
+ * pointer, as that may change before we use it.
+ */
+ if ( config_ptr != NULL ) {
+
+ trace_config = *config_ptr;
+
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ if ( ( cache_ptr == NULL )
+#ifdef H5_HAVE_PARALLEL
+ ||
+ ( ( cache_ptr->aux_ptr != NULL )
+ &&
+ (
+ ((H5AC2_aux_t *)(cache_ptr->aux_ptr))->magic
+ !=
+ H5AC2__H5AC2_AUX_T_MAGIC
+ )
+ )
+#endif /* H5_HAVE_PARALLEL */
+ ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "bad cache_ptr on entry.")
+ }
+
+ result = H5AC2_validate_config(config_ptr);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Bad cache configuration");
+ }
+
+ if ( config_ptr->open_trace_file ) {
+
+ FILE * file_ptr = NULL;
+
+ if ( H5C2_get_trace_file_ptr(cache_ptr, &file_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_get_trace_file_ptr() failed.")
+ }
+
+ if ( ( ! ( config_ptr->close_trace_file ) ) &&
+ ( file_ptr != NULL ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "Trace file already open.")
+ }
+ }
+
+ if (
+ (
+ config_ptr->dirty_bytes_threshold
+ <
+ H5AC2__MIN_DIRTY_BYTES_THRESHOLD
+ )
+ ||
+ (
+ config_ptr->dirty_bytes_threshold
+ >
+ H5AC2__MAX_DIRTY_BYTES_THRESHOLD
+ )
+ ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
+ "config_ptr->dirty_bytes_threshold out of range.")
+ }
+
+ if ( config_ptr->close_trace_file ) {
+
+ if ( H5AC2_close_trace_file(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5AC2_close_trace_file() failed.")
+ }
+ }
+
+ if ( config_ptr->open_trace_file ) {
+
+ if ( H5AC2_open_trace_file(cache_ptr, config_ptr->trace_file_name) < 0 )
+ {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "H5AC2_open_trace_file() failed.")
+ }
+ }
+
+ if ( H5AC2_ext_config_2_int_config(config_ptr, &internal_config) !=
+ SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5AC2_ext_config_2_int_config() failed.")
+ }
+
+ result = H5C2_set_cache_auto_resize_config((H5C2_t *)cache_ptr,
+ &internal_config);
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_set_cache_auto_resize_config() failed.")
+ }
+
+
+ result = H5C2_set_evictions_enabled((H5C2_t *)cache_ptr,
+ config_ptr->evictions_enabled);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_set_evictions_enabled() failed.")
+ }
+
+#ifdef H5_HAVE_PARALLEL
+ if ( cache_ptr->aux_ptr != NULL ) {
+
+ ((H5AC2_aux_t *)(cache_ptr->aux_ptr))->dirty_bytes_threshold =
+ config_ptr->dirty_bytes_threshold;
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+done:
+
+#if H5AC2__TRACE_FILE_ENABLED
+ /* For the set cache auto resize config call, only the contents
+ * of the config is necessary in the trace file. Write the return
+ * value to catch occult errors.
+ */
+ if ( ( cache_ptr != NULL ) &&
+ ( H5C2_get_trace_file_ptr(cache_ptr, &trace_file_ptr) >= 0 ) &&
+ ( trace_file_ptr != NULL ) ) {
+
+ HDfprintf(trace_file_ptr,
+ "%s %d %d %d %d \"%s\" %d %d %d %f %d %d %ld %d %f %f %d %d %d %f %f %d %d %d %d %f %d %d\n",
+ "H5AC2_set_cache_auto_resize_config",
+ trace_config.version,
+ (int)(trace_config.rpt_fcn_enabled),
+ (int)(trace_config.open_trace_file),
+ (int)(trace_config.close_trace_file),
+ trace_config.trace_file_name,
+ (int)(trace_config.evictions_enabled),
+ (int)(trace_config.set_initial_size),
+ (int)(trace_config.initial_size),
+ trace_config.min_clean_fraction,
+ (int)(trace_config.max_size),
+ (int)(trace_config.min_size),
+ trace_config.epoch_length,
+ (int)(trace_config.incr_mode),
+ trace_config.lower_hr_threshold,
+ trace_config.increment,
+ (int)(trace_config.apply_max_increment),
+ (int)(trace_config.max_increment),
+ (int)(trace_config.decr_mode),
+ trace_config.upper_hr_threshold,
+ trace_config.decrement,
+ (int)(trace_config.apply_max_decrement),
+ (int)(trace_config.max_decrement),
+ trace_config.epochs_before_eviction,
+ (int)(trace_config.apply_empty_reserve),
+ trace_config.empty_reserve,
+ trace_config.dirty_bytes_threshold,
+ (int)ret_value);
+ }
+#endif /* H5AC2__TRACE_FILE_ENABLED */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_set_cache_auto_resize_config() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_validate_config()
+ *
+ * Purpose: Run a sanity check on the contents of the supplied
+ * instance of H5AC2_cache_config_t.
+ *
+ * Do nothing and return SUCCEED if no errors are detected,
+ * and flag an error and return FAIL otherwise.
+ *
+ * At present, this function operates by packing the data
+ * from the instance of H5AC2_cache_config_t into an instance
+ * of H5C2_auto_size_ctl_t, and then calling
+ * H5C2_validate_resize_config(). As H5AC2_cache_config_t and
+ * H5C2_auto_size_ctl_t diverge, we may have to change this.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 4/6/05
+ *
+ * Modifications:
+ *
+ * - Added code testing the trace file configuration fields.
+ * These tests are not comprehensive, as many errors cannot
+ * be caught until the directives contained in these fields
+ * are applied.
+ * JRM - 5/15/06
+ *
+ * - Added code testing the evictions enabled field. At
+ * present this consists of verifying that if
+ * evictions_enabled is FALSE, then automatic cache
+ * resizing in disabled.
+ *
+ * JRM - 7/28/07
+ *
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ * JRM - 10/18/07
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_validate_config(H5AC2_cache_config_t * config_ptr)
+
+{
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+ int name_len;
+ H5C2_auto_size_ctl_t internal_config;
+
+ FUNC_ENTER_NOAPI(H5AC2_validate_config, FAIL)
+
+ if ( config_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL config_ptr on entry.")
+ }
+
+ if ( config_ptr->version != H5AC2__CURR_CACHE_CONFIG_VERSION ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Unknown config version.")
+ }
+
+ if ( ( config_ptr->rpt_fcn_enabled != TRUE ) &&
+ ( config_ptr->rpt_fcn_enabled != FALSE ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "config_ptr->rpt_fcn_enabled must be either TRUE or FALSE.")
+ }
+
+ if ( ( config_ptr->open_trace_file != TRUE ) &&
+ ( config_ptr->open_trace_file != FALSE ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "config_ptr->open_trace_file must be either TRUE or FALSE.")
+ }
+
+ if ( ( config_ptr->close_trace_file != TRUE ) &&
+ ( config_ptr->close_trace_file != FALSE ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "config_ptr->close_trace_file must be either TRUE or FALSE.")
+ }
+
+ /* don't bother to test trace_file_name unless open_trace_file is TRUE */
+ if ( config_ptr->open_trace_file ) {
+
+ /* Can't really test the trace_file_name field without trying to
+ * open the file, so we will content ourselves with a couple of
+ * sanity checks on the length of the file name.
+ */
+ name_len = HDstrlen(config_ptr->trace_file_name);
+
+ if ( name_len <= 0 ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "config_ptr->trace_file_name is empty.")
+
+ } else if ( name_len > H5AC2__MAX_TRACE_FILE_NAME_LEN ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "config_ptr->trace_file_name too long.")
+ }
+ }
+
+ if ( ( config_ptr->evictions_enabled != TRUE ) &&
+ ( config_ptr->evictions_enabled != FALSE ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "config_ptr->evictions_enabled must be either TRUE or FALSE.")
+ }
+
+ if ( ( config_ptr->evictions_enabled == FALSE ) &&
+ ( ( config_ptr->incr_mode != H5C2_incr__off ) ||
+ ( config_ptr->incr_mode != H5C2_decr__off ) ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "Can't disable evictions while auto-resize is enabled.")
+ }
+
+ if ( config_ptr->dirty_bytes_threshold < H5AC2__MIN_DIRTY_BYTES_THRESHOLD ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
+ "dirty_bytes_threshold too small.")
+ } else
+ if ( config_ptr->dirty_bytes_threshold > H5AC2__MAX_DIRTY_BYTES_THRESHOLD ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
+ "dirty_bytes_threshold too big.")
+ }
+
+ if ( H5AC2_ext_config_2_int_config(config_ptr, &internal_config) !=
+ SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5AC2_ext_config_2_int_config() failed.")
+ }
+
+ result = H5C2_validate_resize_config(&internal_config,
+ H5C2_RESIZE_CFG__VALIDATE_ALL);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "error(s) in new config.")
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_validate_config() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_close_trace_file()
+ *
+ * Purpose: If a trace file is open, stop logging calls to the cache,
+ * and close the file.
+ *
+ * Note that the function does nothing if there is no trace
+ * file.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/2/06
+ *
+ * Modifications:
+ *
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ * JRM - 10/18/07
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_close_trace_file(H5AC2_t * cache_ptr)
+
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ FILE * trace_file_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5AC2_close_trace_file, FAIL)
+
+ if ( cache_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL cache_ptr on entry.")
+ }
+
+ if ( H5C2_get_trace_file_ptr(cache_ptr, &trace_file_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_get_trace_file_ptr() failed.")
+ }
+
+ if ( trace_file_ptr != NULL ) {
+
+ if ( H5C2_set_trace_file_ptr(cache_ptr, NULL) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_set_trace_file_ptr() failed.")
+ }
+
+ if ( HDfclose(trace_file_ptr) != 0 ) {
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, \
+ "can't close metadata cache trace file")
+ }
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_close_trace_file() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_open_trace_file()
+ *
+ * Purpose: Open a trace file, and start logging calls to the cache.
+ *
+ * This logging is done at the H5C2 level, and will only take
+ * place if H5C2_TRACE_FILE_ENABLED (defined in H5C2private.h)
+ * is TRUE.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/1/06
+ *
+ * Modifications:
+ *
+ * Modified code in support of revised cache API needed
+ * to permit journaling.
+ * JRM - 10/18/07
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_open_trace_file(H5AC2_t * cache_ptr,
+ const char * trace_file_name)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ char file_name[H5AC2__MAX_TRACE_FILE_NAME_LEN + H5C2__PREFIX_LEN + 2];
+ FILE * file_ptr = NULL;
+#ifdef H5_HAVE_PARALLEL
+ H5AC2_aux_t * aux_ptr = NULL;
+#endif /* H5_HAVE_PARALLEL */
+
+ FUNC_ENTER_NOAPI(H5AC2_open_trace_file, FAIL)
+
+ HDassert(cache_ptr);
+
+ if ( cache_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cache_ptr NULL on entry.")
+ }
+
+ if ( trace_file_name == NULL ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "NULL trace_file_name on entry.")
+ }
+
+ if ( HDstrlen(trace_file_name) > H5AC2__MAX_TRACE_FILE_NAME_LEN ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "trace file name too long.")
+ }
+
+ if ( H5C2_get_trace_file_ptr(cache_ptr, &file_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_get_trace_file_ptr() failed.")
+ }
+
+ if ( file_ptr != NULL ) {
+
+ HGOTO_ERROR(H5E_FILE, H5E_FILEOPEN, FAIL, "trace file already open.")
+ }
+
+#ifdef H5_HAVE_PARALLEL
+
+ aux_ptr = (H5AC2_aux_t *)(cache_ptr->aux_ptr);
+
+ if ( cache_ptr->aux_ptr == NULL ) {
+
+ sprintf(file_name, "%s", trace_file_name);
+
+ } else {
+
+ if ( aux_ptr->magic != H5AC2__H5AC2_AUX_T_MAGIC ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad aux_ptr->magic.")
+ }
+
+ sprintf(file_name, "%s.%d", trace_file_name, aux_ptr->mpi_rank);
+
+ }
+
+ if ( HDstrlen(file_name) >
+ H5AC2__MAX_TRACE_FILE_NAME_LEN + H5C2__PREFIX_LEN + 1 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "cooked trace file name too long.")
+ }
+
+#else /* H5_HAVE_PARALLEL */
+
+ sprintf(file_name, "%s", trace_file_name);
+
+#endif /* H5_HAVE_PARALLEL */
+
+ if ( (file_ptr = HDfopen(file_name, "w")) == NULL ) {
+
+ /* trace file open failed */
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "trace file open failed.")
+ }
+
+ HDfprintf(file_ptr, "### HDF5 metadata cache trace file version 1 ###\n");
+
+ if ( H5C2_set_trace_file_ptr(cache_ptr, file_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_set_trace_file_ptr() failed.")
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_open_trace_file() */
+
+
+/*************************************************************************/
+/**************************** Private Functions: *************************/
+/*************************************************************************/
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5AC2_broadcast_clean_list()
+ *
+ * Purpose: Broadcast the contents of the process 0 cleaned entry
+ * slist. In passing, also remove all entries from said
+ * list, and also remove any matching entries from the dirtied
+ * slist.
+ *
+ * This function must only be called by the process with
+ * MPI_rank 0.
+ *
+ * Return SUCCEED on success, and FAIL on failure.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 7/1/05
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+static herr_t
+H5AC2_broadcast_clean_list(H5AC2_t * cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ haddr_t addr;
+ H5AC2_aux_t * aux_ptr = NULL;
+ H5SL_node_t * slist_node_ptr = NULL;
+ H5AC2_slist_entry_t * slist_entry_ptr = NULL;
+ MPI_Offset * buf_ptr = NULL;
+ size_t buf_size;
+ int i = 0;
+ int mpi_result;
+ int num_entries;
+
+ FUNC_ENTER_NOAPI(H5AC2_broadcast_clean_list, FAIL)
+
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ aux_ptr = (H5AC2_aux_t *)(cache_ptr->aux_ptr);
+
+ HDassert( aux_ptr != NULL );
+ HDassert( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+ HDassert( aux_ptr->mpi_rank == 0 );
+ HDassert( aux_ptr->c_slist_ptr != NULL );
+ HDassert( H5SL_count(aux_ptr->c_slist_ptr) ==
+ (size_t)(aux_ptr->c_slist_len) );
+
+
+ /* First broadcast the number of entries in the list so that the
+ * receives can set up a buffer to receive them. If there aren't
+ * any, we are done.
+ */
+ num_entries = aux_ptr->c_slist_len;
+
+ mpi_result = MPI_Bcast(&num_entries, 1, MPI_INT, 0, aux_ptr->mpi_comm);
+
+ if ( mpi_result != MPI_SUCCESS ) {
+
+ HMPI_GOTO_ERROR(FAIL, "MPI_Bcast failed 1", mpi_result)
+
+ }
+
+ if ( num_entries > 0 )
+ {
+ /* allocate a buffer to store the list of entry base addresses in */
+
+ buf_size = sizeof(MPI_Offset) * (size_t)num_entries;
+
+ buf_ptr = (MPI_Offset *)H5MM_malloc(buf_size);
+
+ if ( buf_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "memory allocation failed for clean entry buffer")
+ }
+
+ /* now load the entry base addresses into the buffer, emptying the
+ * cleaned entry list in passing
+ */
+
+ while ( NULL != (slist_node_ptr = H5SL_first(aux_ptr->c_slist_ptr) ) )
+ {
+ slist_entry_ptr = H5SL_item(slist_node_ptr);
+
+ HDassert(slist_entry_ptr->magic == H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC);
+
+ HDassert( i < num_entries );
+
+ addr = slist_entry_ptr->addr;
+
+ if ( H5FD_mpi_haddr_to_MPIOff(addr, &(buf_ptr[i])) < 0 ) {
+
+ HGOTO_ERROR(H5E_INTERNAL, H5E_BADRANGE, FAIL, \
+ "can't convert from haddr to MPI off")
+ }
+
+ i++;
+
+ /* now remove the entry from the cleaned entry list */
+ if ( H5SL_remove(aux_ptr->c_slist_ptr, (void *)(&addr))
+ != slist_entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDELETE, FAIL, \
+ "Can't delete entry from cleaned entry slist.")
+ }
+
+ slist_entry_ptr->magic = 0;
+ H5FL_FREE(H5AC2_slist_entry_t, slist_entry_ptr);
+ slist_entry_ptr = NULL;
+
+ aux_ptr->c_slist_len -= 1;
+
+ HDassert( aux_ptr->c_slist_len >= 0 );
+
+ /* and also remove the matching entry from the dirtied list
+ * if it exists.
+ */
+ if ( (slist_entry_ptr = H5SL_search(aux_ptr->d_slist_ptr,
+ (void *)(&addr))) != NULL ) {
+
+ HDassert( slist_entry_ptr->magic ==
+ H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC );
+ HDassert( slist_entry_ptr->addr == addr );
+
+ if ( H5SL_remove(aux_ptr->d_slist_ptr, (void *)(&addr))
+ != slist_entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDELETE, FAIL, \
+ "Can't delete entry from dirty entry slist.")
+ }
+
+ slist_entry_ptr->magic = 0;
+ H5FL_FREE(H5AC2_slist_entry_t, slist_entry_ptr);
+ slist_entry_ptr = NULL;
+
+ aux_ptr->d_slist_len -= 1;
+
+ HDassert( aux_ptr->d_slist_len >= 0 );
+ }
+
+ } /* while */
+
+
+ /* Now broadcast the list of cleaned entries -- if there is one.
+ *
+ * The peculiar structure of the following call to MPI_Bcast is
+ * due to MPI's (?) failure to believe in the MPI_Offset type.
+ * Thus the element type is MPI_BYTE, with size equal to the
+ * buf_size computed above.
+ */
+
+ mpi_result = MPI_Bcast((void *)buf_ptr, (int)buf_size, MPI_BYTE, 0,
+ aux_ptr->mpi_comm);
+
+ if ( mpi_result != MPI_SUCCESS ) {
+
+ HMPI_GOTO_ERROR(FAIL, "MPI_Bcast failed 2", mpi_result)
+ }
+ }
+
+done:
+
+ if ( buf_ptr != NULL ) {
+
+ buf_ptr = (MPI_Offset *)H5MM_xfree((void *)buf_ptr);
+ }
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_broadcast_clean_list() */
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5AC2_check_if_write_permitted
+ *
+ * Purpose: Determine if a write is permitted under the current
+ * circumstances, and set *write_permitted_ptr accordingly.
+ * As a general rule it is, but when we are running in parallel
+ * mode with collective I/O, we must ensure that a read cannot
+ * cause a write.
+ *
+ * In the event of failure, the value of *write_permitted_ptr
+ * is undefined.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 5/15/04
+ *
+ * Modifications:
+ *
+ * John Mainzer, 9/23/05
+ * Rewrote function to return the value of the
+ * write_permitted field in aux structure if the structure
+ * exists and mpi_rank is 0.
+ *
+ * If the aux structure exists, but mpi_rank isn't 0, the
+ * function now returns FALSE.
+ *
+ * In all other cases, the function returns TRUE.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+static herr_t
+H5AC2_check_if_write_permitted(const H5F_t *f,
+ hid_t UNUSED dxpl_id,
+ hbool_t * write_permitted_ptr)
+#else /* H5_HAVE_PARALLEL */
+static herr_t
+H5AC2_check_if_write_permitted(const H5F_t UNUSED * f,
+ hid_t UNUSED dxpl_id,
+ hbool_t * write_permitted_ptr)
+#endif /* H5_HAVE_PARALLEL */
+{
+ hbool_t write_permitted = TRUE;
+ herr_t ret_value = SUCCEED; /* Return value */
+#ifdef H5_HAVE_PARALLEL
+ H5AC2_aux_t * aux_ptr = NULL;
+#endif /* H5_HAVE_PARALLEL */
+
+
+ FUNC_ENTER_NOAPI(H5AC2_check_if_write_permitted, FAIL)
+
+#ifdef H5_HAVE_PARALLEL
+ HDassert( f != NULL );
+ HDassert( f->shared != NULL );
+ HDassert( f->shared->cache2 != NULL );
+
+ aux_ptr = (H5AC2_aux_t *)(f->shared->cache2->aux_ptr);
+
+ if ( aux_ptr != NULL ) {
+
+ HDassert( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+
+ if ( aux_ptr->mpi_rank == 0 ) {
+
+ write_permitted = aux_ptr->write_permitted;
+
+ } else {
+
+ write_permitted = FALSE;
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ *write_permitted_ptr = write_permitted;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_check_if_write_permitted() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_ext_config_2_int_config()
+ *
+ * Purpose: Utility function to translate an instance of
+ * H5AC2_cache_config_t to an instance of H5C2_auto_size_ctl_t.
+ *
+ * Places translation in *int_conf_ptr and returns SUCCEED
+ * if successful. Returns FAIL on failure.
+ *
+ * Does only minimal sanity checking.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 1/26/06
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5AC2_ext_config_2_int_config(H5AC2_cache_config_t * ext_conf_ptr,
+ H5C2_auto_size_ctl_t * int_conf_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5AC2_ext_config_2_int_config, FAIL)
+
+ if ( ( ext_conf_ptr == NULL ) ||
+ ( ext_conf_ptr->version != H5AC2__CURR_CACHE_CONFIG_VERSION ) ||
+ ( int_conf_ptr == NULL ) ) {
+
+ }
+
+ int_conf_ptr->version = H5C2__CURR_AUTO_SIZE_CTL_VER;
+
+ if ( ext_conf_ptr->rpt_fcn_enabled ) {
+
+ int_conf_ptr->rpt_fcn = H5C2_def_auto_resize_rpt_fcn;
+
+ } else {
+
+ int_conf_ptr->rpt_fcn = NULL;
+ }
+
+ int_conf_ptr->set_initial_size = ext_conf_ptr->set_initial_size;
+ int_conf_ptr->initial_size = ext_conf_ptr->initial_size;
+ int_conf_ptr->min_clean_fraction = ext_conf_ptr->min_clean_fraction;
+ int_conf_ptr->max_size = ext_conf_ptr->max_size;
+ int_conf_ptr->min_size = ext_conf_ptr->min_size;
+ int_conf_ptr->epoch_length =
+ (int64_t)(ext_conf_ptr->epoch_length);
+
+ int_conf_ptr->incr_mode = ext_conf_ptr->incr_mode;
+ int_conf_ptr->lower_hr_threshold = ext_conf_ptr->lower_hr_threshold;
+ int_conf_ptr->increment = ext_conf_ptr->increment;
+ int_conf_ptr->apply_max_increment = ext_conf_ptr->apply_max_increment;
+ int_conf_ptr->max_increment = ext_conf_ptr->max_increment;
+
+ int_conf_ptr->decr_mode = ext_conf_ptr->decr_mode;
+ int_conf_ptr->upper_hr_threshold = ext_conf_ptr->upper_hr_threshold;
+ int_conf_ptr->decrement = ext_conf_ptr->decrement;
+ int_conf_ptr->apply_max_decrement = ext_conf_ptr->apply_max_decrement;
+ int_conf_ptr->max_decrement = ext_conf_ptr->max_decrement;
+ int_conf_ptr->epochs_before_eviction =
+ (int32_t)(ext_conf_ptr->epochs_before_eviction);
+ int_conf_ptr->apply_empty_reserve = ext_conf_ptr->apply_empty_reserve;
+ int_conf_ptr->empty_reserve = ext_conf_ptr->empty_reserve;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_ext_config_2_int_config() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5AC2_log_deleted_entry()
+ *
+ * Purpose: Log an entry for which H5C2__DELETED_FLAG has been set.
+ *
+ * If mpi_rank is 0, we must make sure that the entry doesn't
+ * appear in the cleaned or dirty entry lists. Otherwise,
+ * we have nothing to do.
+ *
+ * Return SUCCEED on success, and FAIL on failure.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 6/29/05
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+static herr_t
+H5AC2_log_deleted_entry(H5AC2_t * cache_ptr,
+ H5AC2_info_t * entry_ptr,
+ haddr_t addr,
+ unsigned int flags)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5AC2_aux_t * aux_ptr = NULL;
+ H5AC2_slist_entry_t * slist_entry_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5AC2_log_deleted_entry, FAIL)
+
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ aux_ptr = cache_ptr->aux_ptr;
+
+ HDassert( aux_ptr != NULL );
+ HDassert( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+
+ HDassert( entry_ptr != NULL );
+ HDassert( entry_ptr->addr == addr );
+
+ HDassert( (flags & H5C2__DELETED_FLAG) != 0 );
+
+ if ( aux_ptr->mpi_rank == 0 ) {
+
+ HDassert( aux_ptr->d_slist_ptr != NULL );
+ HDassert( aux_ptr->c_slist_ptr != NULL );
+
+ /* if the entry appears in the dirtied entry slist, remove it. */
+ if ( (slist_entry_ptr = H5SL_search(aux_ptr->d_slist_ptr,
+ (void *)(&addr))) != NULL ) {
+
+ HDassert( slist_entry_ptr->magic ==
+ H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC );
+ HDassert( slist_entry_ptr->addr == addr );
+
+ if ( H5SL_remove(aux_ptr->d_slist_ptr, (void *)(&addr))
+ != slist_entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDELETE, FAIL, \
+ "Can't delete entry from dirty entry slist.")
+ }
+
+ slist_entry_ptr->magic = 0;
+ H5FL_FREE(H5AC2_slist_entry_t, slist_entry_ptr);
+ slist_entry_ptr = NULL;
+
+ aux_ptr->d_slist_len -= 1;
+
+ HDassert( aux_ptr->d_slist_len >= 0 );
+ }
+
+ /* if the entry appears in the cleaned entry slist, remove it. */
+ if ( (slist_entry_ptr = H5SL_search(aux_ptr->c_slist_ptr,
+ (void *)(&addr))) != NULL ) {
+
+ HDassert( slist_entry_ptr->magic ==
+ H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC );
+ HDassert( slist_entry_ptr->addr == addr );
+
+ if ( H5SL_remove(aux_ptr->c_slist_ptr, (void *)(&addr))
+ != slist_entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDELETE, FAIL, \
+ "Can't delete entry from cleaned entry slist.")
+ }
+
+ slist_entry_ptr->magic = 0;
+ H5FL_FREE(H5AC2_slist_entry_t, slist_entry_ptr);
+ slist_entry_ptr = NULL;
+
+ aux_ptr->c_slist_len -= 1;
+
+ HDassert( aux_ptr->c_slist_len >= 0 );
+ }
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_log_deleted_entry() */
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5AC2_log_dirtied_entry()
+ *
+ * Purpose: Update the dirty_bytes count for a newly dirtied entry.
+ *
+ * If mpi_rank isnt 0, this simply means adding the size
+ * of the entries to the dirty_bytes count.
+ *
+ * If mpi_rank is 0, we must first check to see if the entry
+ * appears in the dirty entries slist. If it is, do nothing.
+ * If it isn't, add the size to th dirty_bytes count, add the
+ * entry to the dirty entries slist, and remove it from the
+ * cleaned list (if it is present there).
+ *
+ * Return SUCCEED on success, and FAIL on failure.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 6/29/05
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+static herr_t
+H5AC2_log_dirtied_entry(H5AC2_t * cache_ptr,
+ H5AC2_info_t * entry_ptr,
+ haddr_t addr,
+ hbool_t size_changed,
+ size_t new_size)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ size_t entry_size;
+ H5AC2_aux_t * aux_ptr = NULL;
+ H5AC2_slist_entry_t * slist_entry_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5AC2_log_dirtied_entry, FAIL)
+
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ aux_ptr = cache_ptr->aux_ptr;
+
+ HDassert( aux_ptr != NULL );
+ HDassert( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+
+ HDassert( entry_ptr != NULL );
+ HDassert( entry_ptr->addr == addr );
+ HDassert( entry_ptr->is_dirty == FALSE );
+
+ if ( size_changed ) {
+
+ entry_size = new_size;
+
+ } else {
+
+ entry_size = entry_ptr->size;
+ }
+
+ if ( aux_ptr->mpi_rank == 0 ) {
+
+ HDassert( aux_ptr->d_slist_ptr != NULL );
+ HDassert( aux_ptr->c_slist_ptr != NULL );
+
+ if ( H5SL_search(aux_ptr->d_slist_ptr, (void *)(&addr)) == NULL ) {
+
+ /* insert the address of the entry in the dirty entry list, and
+ * add its size to the dirty_bytes count.
+ */
+ if ( NULL == (slist_entry_ptr = H5FL_CALLOC(H5AC2_slist_entry_t)) ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "Can't allocate dirty slist entry .")
+ }
+
+ slist_entry_ptr->magic = H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC;
+ slist_entry_ptr->addr = addr;
+
+ if ( H5SL_insert(aux_ptr->d_slist_ptr, slist_entry_ptr,
+ &(slist_entry_ptr->addr)) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINSERT, FAIL, \
+ "can't insert entry into dirty entry slist.")
+ }
+
+ aux_ptr->d_slist_len += 1;
+ aux_ptr->dirty_bytes += entry_size;
+#if H5AC2_DEBUG_DIRTY_BYTES_CREATION
+ aux_ptr->unprotect_dirty_bytes += entry_size;
+ aux_ptr->unprotect_dirty_bytes_updates += 1;
+#endif /* H5AC2_DEBUG_DIRTY_BYTES_CREATION */
+ }
+
+ if ( H5SL_search(aux_ptr->c_slist_ptr, (void *)(&addr)) != NULL ) {
+
+ /* the entry is dirty. If it exists on the cleaned entries list,
+ * remove it.
+ */
+ if ( (slist_entry_ptr = H5SL_search(aux_ptr->c_slist_ptr,
+ (void *)(&addr))) != NULL ) {
+
+ HDassert( slist_entry_ptr->magic ==
+ H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC );
+ HDassert( slist_entry_ptr->addr == addr );
+
+ if ( H5SL_remove(aux_ptr->c_slist_ptr, (void *)(&addr))
+ != slist_entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDELETE, FAIL, \
+ "Can't delete entry from clean entry slist.")
+ }
+
+ slist_entry_ptr->magic = 0;
+ H5FL_FREE(H5AC2_slist_entry_t, slist_entry_ptr);
+ slist_entry_ptr = NULL;
+
+ aux_ptr->c_slist_len -= 1;
+
+ HDassert( aux_ptr->c_slist_len >= 0 );
+ }
+ }
+ } else {
+
+ aux_ptr->dirty_bytes += entry_size;
+#if H5AC2_DEBUG_DIRTY_BYTES_CREATION
+ aux_ptr->unprotect_dirty_bytes += entry_size;
+ aux_ptr->unprotect_dirty_bytes_updates += 1;
+#endif /* H5AC2_DEBUG_DIRTY_BYTES_CREATION */
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_log_dirtied_entry() */
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5AC2_log_flushed_entry()
+ *
+ * Purpose: Update the clean entry slist for the flush of an entry --
+ * specifically, if the entry has been cleared, remove it
+ * from both the cleaned and dirtied lists if it is present.
+ * Otherwise, if the entry was dirty, insert the indicated
+ * entry address in the clean slist if it isn't there already.
+ *
+ * This function is only used in PHDF5, and should only
+ * be called for the process with mpi rank 0.
+ *
+ * Return SUCCEED on success, and FAIL on failure.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 6/29/05
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+#if 0 /* This is useful debugging code. -- JRM */
+static herr_t
+H5AC2_log_flushed_entry_dummy(H5C2_t * cache_ptr,
+ haddr_t addr,
+ hbool_t was_dirty,
+ unsigned flags,
+ int type_id)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5AC2_aux_t * aux_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5AC2_log_flushed_entry_dummy, FAIL)
+
+ aux_ptr = cache_ptr->aux_ptr;
+
+ if ( ( was_dirty ) && ( (flags & H5C2__FLUSH_CLEAR_ONLY_FLAG) == 0 ) ) {
+
+ HDfprintf(stdout,
+ "%d:H5AC2_log_flushed_entry(): addr = %d, flags = %x, was_dirty = %d, type_id = %d\n",
+ (int)(aux_ptr->mpi_rank), (int)addr, flags, (int)was_dirty, type_id);
+ }
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_log_flushed_entry_dummy() */
+#endif /* JRM */
+
+static herr_t
+H5AC2_log_flushed_entry(H5C2_t * cache_ptr,
+ haddr_t addr,
+ hbool_t was_dirty,
+ unsigned flags,
+ int UNUSED type_id)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ hbool_t cleared;
+ H5AC2_aux_t * aux_ptr;
+ H5AC2_slist_entry_t * slist_entry_ptr = NULL;
+
+
+ FUNC_ENTER_NOAPI(H5AC2_log_flushed_entry, FAIL)
+
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ aux_ptr = cache_ptr->aux_ptr;
+
+ HDassert( aux_ptr != NULL );
+ HDassert( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+ HDassert( aux_ptr->mpi_rank == 0 );
+ HDassert( aux_ptr->c_slist_ptr != NULL );
+
+ cleared = ( (flags & H5C2__FLUSH_CLEAR_ONLY_FLAG) != 0 );
+
+ if ( cleared ) {
+
+ /* If the entry has been cleared, must remove it from both the
+ * cleaned list and the dirtied list.
+ */
+
+ if ( (slist_entry_ptr = H5SL_search(aux_ptr->c_slist_ptr,
+ (void *)(&addr))) != NULL ) {
+
+ HDassert( slist_entry_ptr->magic == H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC);
+ HDassert( slist_entry_ptr->addr == addr );
+
+ if ( H5SL_remove(aux_ptr->c_slist_ptr, (void *)(&addr))
+ != slist_entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDELETE, FAIL, \
+ "Can't delete entry from clean entry slist.")
+ }
+
+ slist_entry_ptr->magic = 0;
+ H5FL_FREE(H5AC2_slist_entry_t, slist_entry_ptr);
+ slist_entry_ptr = NULL;
+
+ aux_ptr->c_slist_len -= 1;
+
+ HDassert( aux_ptr->c_slist_len >= 0 );
+ }
+
+ if ( (slist_entry_ptr = H5SL_search(aux_ptr->d_slist_ptr,
+ (void *)(&addr))) != NULL ) {
+
+ HDassert( slist_entry_ptr->magic == H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC);
+ HDassert( slist_entry_ptr->addr == addr );
+
+ if ( H5SL_remove(aux_ptr->d_slist_ptr, (void *)(&addr))
+ != slist_entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDELETE, FAIL, \
+ "Can't delete entry from dirty entry slist.")
+ }
+
+ slist_entry_ptr->magic = 0;
+ H5FL_FREE(H5AC2_slist_entry_t, slist_entry_ptr);
+ slist_entry_ptr = NULL;
+
+ aux_ptr->d_slist_len -= 1;
+
+ HDassert( aux_ptr->d_slist_len >= 0 );
+ }
+ } else if ( was_dirty ) {
+
+ if ( H5SL_search(aux_ptr->c_slist_ptr, (void *)(&addr)) == NULL ) {
+
+ /* insert the address of the entry in the clean entry list. */
+
+ if ( NULL == (slist_entry_ptr = H5FL_CALLOC(H5AC2_slist_entry_t)) ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "Can't allocate clean slist entry .")
+ }
+
+ slist_entry_ptr->magic = H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC;
+ slist_entry_ptr->addr = addr;
+
+ if ( H5SL_insert(aux_ptr->c_slist_ptr, slist_entry_ptr,
+ &(slist_entry_ptr->addr)) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINSERT, FAIL, \
+ "can't insert entry into clean entry slist.")
+ }
+
+ aux_ptr->c_slist_len += 1;
+ }
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_log_flushed_entry() */
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5AC2_log_inserted_entry()
+ *
+ * Purpose: Update the dirty_bytes count for a newly inserted entry.
+ *
+ * If mpi_rank isnt 0, this simply means adding the size
+ * of the entry to the dirty_bytes count.
+ *
+ * If mpi_rank is 0, we must also add the entry to the
+ * dirty entries slist.
+ *
+ * Return SUCCEED on success, and FAIL on failure.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 6/30/05
+ *
+ * Modifications:
+ *
+ * JRM -- 10/24/07
+ * Added the size parameter.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+static herr_t
+H5AC2_log_inserted_entry(H5F_t * f,
+ H5AC2_t * cache_ptr,
+ H5AC2_info_t * entry_ptr,
+ const H5AC2_class_t * type,
+ haddr_t addr,
+ size_t size)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5AC2_aux_t * aux_ptr = NULL;
+ H5AC2_slist_entry_t * slist_entry_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5AC2_log_inserted_entry, FAIL)
+
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ aux_ptr = cache_ptr->aux_ptr;
+
+ HDassert( aux_ptr != NULL );
+ HDassert( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+
+ HDassert( entry_ptr != NULL );
+ HDassert( entry_ptr->addr == addr );
+ HDassert( entry_ptr->type == type );
+
+ if ( aux_ptr->mpi_rank == 0 ) {
+
+ HDassert( aux_ptr->d_slist_ptr != NULL );
+ HDassert( aux_ptr->c_slist_ptr != NULL );
+
+ if ( H5SL_search(aux_ptr->d_slist_ptr, (void *)(&addr)) == NULL ) {
+
+ /* insert the address of the entry in the dirty entry list, and
+ * add its size to the dirty_bytes count.
+ */
+ if ( NULL == (slist_entry_ptr = H5FL_CALLOC(H5AC2_slist_entry_t)) ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "Can't allocate dirty slist entry .")
+ }
+
+ slist_entry_ptr->magic = H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC;
+ slist_entry_ptr->addr = addr;
+
+ if ( H5SL_insert(aux_ptr->d_slist_ptr, slist_entry_ptr,
+ &(slist_entry_ptr->addr)) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINSERT, FAIL, \
+ "can't insert entry into dirty entry slist.")
+ }
+
+ aux_ptr->d_slist_len += 1;
+
+ } else {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Inserted entry already in dirty slist.")
+ }
+
+ if ( H5SL_search(aux_ptr->c_slist_ptr, (void *)(&addr)) != NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Inserted entry in clean slist.")
+ }
+ }
+
+ aux_ptr->dirty_bytes += size;
+
+#if H5AC2_DEBUG_DIRTY_BYTES_CREATION
+ aux_ptr->insert_dirty_bytes += size;
+ aux_ptr->insert_dirty_bytes_updates += 1;
+#endif /* H5AC2_DEBUG_DIRTY_BYTES_CREATION */
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_log_inserted_entry() */
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5AC2_log_renamed_entry()
+ *
+ * Purpose: Update the dirty_bytes count for a renamed entry.
+ *
+ * WARNING
+ *
+ * At present, the way that the rename call is used ensures
+ * that the renamed entry is present in all caches by
+ * renaming in a collective operation and immediately after
+ * unprotecting the target entry.
+ *
+ * This function uses this invarient, and will cause arcane
+ * failures if it is not met. If maintaining this invarient
+ * becomes impossible, we will have to rework this function
+ * extensively, and likely include a bit of IPC for
+ * synchronization. A better option might be to subsume
+ * rename in the unprotect operation.
+ *
+ * Given that the target entry is in all caches, the function
+ * proceeds as follows:
+ *
+ * For processes with mpi rank other 0, it simply checks to
+ * see if the entry was dirty prior to the rename, and adds
+ * the entries size to the dirty bytes count.
+ *
+ * In the process with mpi rank 0, the function first checks
+ * to see if the entry was dirty prior to the rename. If it
+ * was, and if the entry doesn't appear in the dirtied list
+ * under its old address, it adds the entry's size to the
+ * dirty bytes count.
+ *
+ * The rank 0 process then removes any references to the
+ * entry under its old address from the cleands and dirtied
+ * lists, and inserts an entry in the dirtied list under the
+ * new address.
+ *
+ * Return SUCCEED on success, and FAIL on failure.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 6/30/05
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+static herr_t
+H5AC2_log_renamed_entry(H5AC2_t * cache_ptr,
+ haddr_t old_addr,
+ haddr_t new_addr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ hbool_t entry_in_cache;
+ hbool_t entry_dirty;
+ size_t entry_size;
+ H5AC2_aux_t * aux_ptr = NULL;
+ H5AC2_slist_entry_t * slist_entry_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5AC2_log_renamed_entry, FAIL)
+
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ aux_ptr = cache_ptr->aux_ptr;
+
+ HDassert( aux_ptr != NULL );
+ HDassert( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+
+ /* get entry status, size, etc here */
+ if ( H5C2_get_entry_status(cache_ptr, old_addr, &entry_size, &entry_in_cache,
+ &entry_dirty, NULL, NULL) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't get entry status.")
+
+ } else if ( ! entry_in_cache ) {
+
+ HDassert( entry_in_cache );
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "entry not in cache.")
+ }
+
+ if ( aux_ptr->mpi_rank == 0 ) {
+
+ HDassert( aux_ptr->d_slist_ptr != NULL );
+ HDassert( aux_ptr->c_slist_ptr != NULL );
+
+ /* if the entry appears in the cleaned entry slist, under its old
+ * address, remove it.
+ */
+ if ( (slist_entry_ptr = H5SL_search(aux_ptr->c_slist_ptr,
+ (void *)(&old_addr))) != NULL ) {
+
+ HDassert( slist_entry_ptr->magic ==
+ H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC );
+ HDassert( slist_entry_ptr->addr == old_addr );
+
+ if ( H5SL_remove(aux_ptr->c_slist_ptr, (void *)(&old_addr))
+ != slist_entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDELETE, FAIL, \
+ "Can't delete entry from cleaned entry slist.")
+ }
+
+ slist_entry_ptr->magic = 0;
+ H5FL_FREE(H5AC2_slist_entry_t, slist_entry_ptr);
+ slist_entry_ptr = NULL;
+
+ aux_ptr->c_slist_len -= 1;
+
+ HDassert( aux_ptr->c_slist_len >= 0 );
+ }
+
+ /* if the entry appears in the dirtied entry slist under its old
+ * address, remove it, but don't free it. Set addr to new_addr.
+ */
+ if ( (slist_entry_ptr = H5SL_search(aux_ptr->d_slist_ptr,
+ (void *)(&old_addr))) != NULL ) {
+
+ HDassert( slist_entry_ptr->magic ==
+ H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC );
+ HDassert( slist_entry_ptr->addr == old_addr );
+
+ if ( H5SL_remove(aux_ptr->d_slist_ptr, (void *)(&old_addr))
+ != slist_entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDELETE, FAIL, \
+ "Can't delete entry from dirty entry slist.")
+ }
+
+ slist_entry_ptr->addr = new_addr;
+
+ aux_ptr->d_slist_len -= 1;
+
+ HDassert( aux_ptr->d_slist_len >= 0 );
+
+ } else {
+
+ /* otherwise, allocate a new entry that is ready
+ * for insertion, and increment dirty_bytes.
+ *
+ * Note that the fact that the entry wasn't in the dirtied
+ * list under its old address implies that it must have
+ * been clean to start with.
+ */
+
+ HDassert( !entry_dirty );
+
+ if ( NULL == (slist_entry_ptr = H5FL_CALLOC(H5AC2_slist_entry_t)) ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "Can't allocate dirty slist entry .")
+ }
+
+ slist_entry_ptr->magic = H5AC2__H5AC2_SLIST_ENTRY_T_MAGIC;
+ slist_entry_ptr->addr = new_addr;
+
+ aux_ptr->dirty_bytes += entry_size;
+
+#if H5AC2_DEBUG_DIRTY_BYTES_CREATION
+ aux_ptr->rename_dirty_bytes += entry_size;
+ aux_ptr->rename_dirty_bytes_updates += 1;
+#endif /* H5AC2_DEBUG_DIRTY_BYTES_CREATION */
+ }
+
+ /* verify that there is no entry at new_addr in the dirty slist */
+ if ( H5SL_search(aux_ptr->d_slist_ptr, (void *)(&new_addr)) != NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "dirty slist already contains entry at new_addr.")
+ }
+
+ /* insert / reinsert the entry in the dirty slist */
+ if ( H5SL_insert(aux_ptr->d_slist_ptr, slist_entry_ptr,
+ &(slist_entry_ptr->addr)) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINSERT, FAIL, \
+ "can't insert entry into dirty entry slist.")
+ }
+
+ aux_ptr->d_slist_len += 1;
+
+ } else if ( ! entry_dirty ) {
+
+ aux_ptr->dirty_bytes += entry_size;
+
+#if H5AC2_DEBUG_DIRTY_BYTES_CREATION
+ aux_ptr->rename_dirty_bytes += entry_size;
+ aux_ptr->rename_dirty_bytes_updates += 1;
+#endif /* H5AC2_DEBUG_DIRTY_BYTES_CREATION */
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_log_renamed_entry() */
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC2_propagate_flushed_and_still_clean_entries_list
+ *
+ * Purpose: In PHDF5, only the metadata cache with mpi rank 0 is allowed
+ * to write to file. All other metadata caches on processes
+ * with rank greater than 0 must retain dirty entries until
+ * they are notified that the entry is now clean.
+ *
+ * This function is the main routine for that proceedure.
+ * It must be called simultaniously on all processes that
+ * have the relevant file open. To this end, there must
+ * be a barrier immediately prior to this call.
+ *
+ * Typicaly, this will be done one of two ways:
+ *
+ * 1) Dirty byte creation exceeds some user specified value.
+ *
+ * While metadata reads may occur independently, all
+ * operations writing metadata must be collective. Thus
+ * all metadata caches see the same sequence of operations,
+ * and therefore the same dirty data creation.
+ *
+ * This fact is used to synchronize the caches for purposes
+ * of propagating the list of flushed and still clean
+ * entries, by simply calling this function from all
+ * caches whenever some user specified threshold on dirty
+ * data is exceeded.
+ *
+ * 2) Under direct user control -- this operation must be
+ * collective.
+ *
+ * The operations to be managed by this function are as
+ * follows:
+ *
+ * For the process with mpi rank 0:
+ *
+ * 1) Enable writes, flush the cache to its min clean size,
+ * and then disable writes again.
+ *
+ * 2) Load the contents of the flushed and still clean entries
+ * list (c_slist_ptr) into a buffer, and broadcast that
+ * buffer to all the other caches.
+ *
+ * 3) Clear the flushed and still clean entries list
+ * (c_slist_ptr).
+ *
+ *
+ * For all processes with mpi rank greater than 0:
+ *
+ * 1) Receive the flushed and still clean entries list broadcast
+ *
+ * 2) Mark the specified entries as clean.
+ *
+ *
+ * For all processes:
+ *
+ * 1) Reset the dirtied bytes count to 0.
+ *
+ * Return: Success: non-negative
+ *
+ * Failure: negative
+ *
+ * Programmer: John Mainzer
+ * July 5, 2005
+ *
+ * Modifications:
+ *
+ * JRM -- 5/11/06
+ * Added code to call the write_done callback.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+herr_t
+H5AC2_propagate_flushed_and_still_clean_entries_list(H5F_t * f,
+ hid_t dxpl_id,
+ H5AC2_t * cache_ptr,
+ hbool_t do_barrier)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ herr_t result;
+ int mpi_code;
+ H5AC2_aux_t * aux_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5AC2_propagate_flushed_and_still_clean_entries_list, FAIL)
+
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ aux_ptr = (H5AC2_aux_t *)(cache_ptr->aux_ptr);
+
+ HDassert( aux_ptr != NULL );
+ HDassert( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+
+#if H5AC2_DEBUG_DIRTY_BYTES_CREATION
+ HDfprintf(stdout,
+ "%d:H5AC2_propagate...:%d: (u/uu/i/iu/r/ru) = %d/%d/%d/%d/%d/%d\n",
+ (int)(aux_ptr->mpi_rank),
+ (int)(aux_ptr->dirty_bytes_propagations),
+ (int)(aux_ptr->unprotect_dirty_bytes),
+ (int)(aux_ptr->unprotect_dirty_bytes_updates),
+ (int)(aux_ptr->insert_dirty_bytes),
+ (int)(aux_ptr->insert_dirty_bytes_updates),
+ (int)(aux_ptr->rename_dirty_bytes),
+ (int)(aux_ptr->rename_dirty_bytes_updates));
+#endif /* H5AC2_DEBUG_DIRTY_BYTES_CREATION */
+
+ if ( do_barrier ) {
+
+ /* to prevent "messages from the future" we must synchronize all
+ * processes before we start the flush. This synchronization may
+ * already be done -- hence the do_barrier parameter.
+ */
+
+ if ( MPI_SUCCESS != (mpi_code = MPI_Barrier(aux_ptr->mpi_comm)) ) {
+
+ HMPI_GOTO_ERROR(FAIL, "MPI_Barrier failed", mpi_code)
+ }
+ }
+
+ if ( aux_ptr->mpi_rank == 0 ) {
+
+ aux_ptr->write_permitted = TRUE;
+
+ result = H5C2_flush_to_min_clean(cache_ptr, dxpl_id);
+
+ aux_ptr->write_permitted = FALSE;
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_flush_to_min_clean() failed.")
+ }
+
+ if ( aux_ptr->write_done != NULL ) {
+
+ (aux_ptr->write_done)();
+ }
+
+ if ( H5AC2_broadcast_clean_list(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Can't broadcast clean slist.")
+ }
+
+ HDassert( aux_ptr->c_slist_len == 0 );
+
+ } else {
+
+ if ( H5AC2_receive_and_apply_clean_list(f, dxpl_id,
+ cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Can't receive and/or process clean slist broadcast.")
+ }
+ }
+
+ aux_ptr->dirty_bytes = 0;
+#if H5AC2_DEBUG_DIRTY_BYTES_CREATION
+ aux_ptr->dirty_bytes_propagations += 1;
+ aux_ptr->unprotect_dirty_bytes = 0;
+ aux_ptr->unprotect_dirty_bytes_updates = 0;
+ aux_ptr->insert_dirty_bytes = 0;
+ aux_ptr->insert_dirty_bytes_updates = 0;
+ aux_ptr->rename_dirty_bytes = 0;
+ aux_ptr->rename_dirty_bytes_updates = 0;
+#endif /* H5AC2_DEBUG_DIRTY_BYTES_CREATION */
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_propagate_flushed_and_still_clean_entries_list() */
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5AC2_receive_and_apply_clean_list()
+ *
+ * Purpose: Receive the list of cleaned entries from process 0,
+ * and mark the specified entries as clean.
+ *
+ * This function must only be called by the process with
+ * MPI_rank greater than 0.
+ *
+ * Return SUCCEED on success, and FAIL on failure.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 7/4/05
+ *
+ * Modifications:
+ *
+ * JRM --10/24/07
+ * Reworked parameter list in support of the revised cache
+ * API.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+static herr_t
+H5AC2_receive_and_apply_clean_list(H5F_t * f,
+ hid_t dxpl_id,
+ H5AC2_t * cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5AC2_aux_t * aux_ptr = NULL;
+ haddr_t * haddr_buf_ptr = NULL;
+ MPI_Offset * MPI_Offset_buf_ptr = NULL;
+ size_t buf_size;
+ int i = 0;
+ int mpi_result;
+ int num_entries;
+
+ FUNC_ENTER_NOAPI(H5AC2_receive_and_apply_clean_list, FAIL)
+
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ aux_ptr = (H5AC2_aux_t *)(cache_ptr->aux_ptr);
+
+ HDassert( aux_ptr != NULL );
+ HDassert( aux_ptr->magic == H5AC2__H5AC2_AUX_T_MAGIC );
+ HDassert( aux_ptr->mpi_rank != 0 );
+
+ /* First receive the number of entries in the list so that we
+ * can set up a buffer to receive them. If there aren't
+ * any, we are done.
+ */
+ mpi_result = MPI_Bcast(&num_entries, 1, MPI_INT, 0, aux_ptr->mpi_comm);
+
+ if ( mpi_result != MPI_SUCCESS ) {
+
+ HMPI_GOTO_ERROR(FAIL, "MPI_Bcast failed 1", mpi_result)
+ }
+
+ if ( num_entries > 0 )
+ {
+ /* allocate a buffers to store the list of entry base addresses in */
+
+ buf_size = sizeof(MPI_Offset) * (size_t)num_entries;
+
+ MPI_Offset_buf_ptr = (MPI_Offset *)H5MM_malloc(buf_size);
+
+ if ( MPI_Offset_buf_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "memory allocation failed for receive buffer")
+ }
+
+ haddr_buf_ptr = (haddr_t *)H5MM_malloc(sizeof(haddr_t) *
+ (size_t)num_entries);
+
+ if ( haddr_buf_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "memory allocation failed for haddr buffer")
+ }
+
+
+ /* Now receive the list of cleaned entries
+ *
+ * The peculiar structure of the following call to MPI_Bcast is
+ * due to MPI's (?) failure to believe in the MPI_Offset type.
+ * Thus the element type is MPI_BYTE, with size equal to the
+ * buf_size computed above.
+ */
+
+ mpi_result = MPI_Bcast((void *)MPI_Offset_buf_ptr, (int)buf_size,
+ MPI_BYTE, 0, aux_ptr->mpi_comm);
+
+ if ( mpi_result != MPI_SUCCESS ) {
+
+ HMPI_GOTO_ERROR(FAIL, "MPI_Bcast failed 2", mpi_result)
+ }
+
+
+ /* translate the MPI_Offsets to haddr_t */
+ i = 0;
+ while ( i < num_entries )
+ {
+ haddr_buf_ptr[i] = H5FD_mpi_MPIOff_to_haddr(MPI_Offset_buf_ptr[i]);
+
+ if ( haddr_buf_ptr[i] == HADDR_UNDEF ) {
+
+ HGOTO_ERROR(H5E_INTERNAL, H5E_BADRANGE, FAIL, \
+ "can't convert MPI off to haddr")
+ }
+
+ i++;
+ }
+
+
+ /* mark the indicated entries as clean */
+ if ( H5C2_mark_entries_as_clean(cache_ptr, dxpl_id,
+ (int32_t)num_entries,
+ &(haddr_buf_ptr[0])) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Can't mark entries clean.")
+
+ }
+ }
+
+done:
+
+ if ( MPI_Offset_buf_ptr != NULL ) {
+
+ MPI_Offset_buf_ptr =
+ (MPI_Offset *)H5MM_xfree((void *)MPI_Offset_buf_ptr);
+ }
+
+ if ( haddr_buf_ptr != NULL ) {
+
+ haddr_buf_ptr = (haddr_t *)H5MM_xfree((void *)haddr_buf_ptr);
+ }
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5AC2_receive_and_apply_clean_list() */
+#endif /* H5_HAVE_PARALLEL */
+
diff --git a/src/H5AC2pkg.h b/src/H5AC2pkg.h
new file mode 100644
index 0000000..202f4e3
--- /dev/null
+++ b/src/H5AC2pkg.h
@@ -0,0 +1,335 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+ * Copyright by the Board of Trustees of the University of Illinois. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * Programmer: John Mainzer -- 4/19/06
+ *
+ * Purpose: This file contains declarations which are normally visible
+ * only within the H5AC2 package (just H5AC2.c at present).
+ *
+ * Source files outside the H5AC package should include
+ * H5ACprivate.h instead.
+ *
+ * The one exception to this rule is testpar/t_cache.c. The
+ * test code is easier to write if it can look at H5AC_aux_t.
+ * Indeed, this is the main reason why this file was created.
+ *
+ * Modifications:
+ *
+ * JRM - 10/18/07
+ * Copied H5ACpkg.h to H5AC2pkg.h and reworked
+ * to use H5C2 instead of H5C. All this is in support
+ * of cache API modifications needed for journaling.
+ *
+ */
+
+#ifndef H5AC2_PACKAGE
+#error "Do not include this file outside the H5AC2 package!"
+#endif
+
+#ifndef _H5AC2pkg_H
+#define _H5AC2pkg_H
+
+#define H5C2_PACKAGE /*suppress error about including H5C2pkg */
+
+/* Get package's private header */
+#include "H5AC2private.h"
+#include "H5C2private.h"
+
+
+/* Get needed headers */
+#include "H5C2pkg.h" /* Cache */
+#include "H5SLprivate.h" /* Skip lists */
+
+
+#define H5AC2_DEBUG_DIRTY_BYTES_CREATION 0
+
+/*-------------------------------------------------------------------------
+ * It is a bit difficult to set ranges of allowable values on the
+ * dirty_bytes_threshold field of H5AC2_aux_t. The following are
+ * probably broader than they should be.
+ *-------------------------------------------------------------------------
+ */
+
+#define H5AC2__MIN_DIRTY_BYTES_THRESHOLD (int32_t) \
+ (H5C2__MIN_MAX_CACHE_SIZE / 2)
+#define H5AC2__DEFAULT_DIRTY_BYTES_THRESHOLD (256 * 1024)
+#define H5AC2__MAX_DIRTY_BYTES_THRESHOLD (int32_t) \
+ (H5C2__MAX_MAX_CACHE_SIZE / 4)
+
+/****************************************************************************
+ *
+ * structure H5AC2_aux_t
+ *
+ * While H5AC2 has become a wrapper for the cache implemented in H5C2.c, there
+ * are some features of the metadata cache that are specific to it, and which
+ * therefore do not belong in the more generic H5C2 cache code.
+ *
+ * In particular, there is the matter of synchronizing writes from the
+ * metadata cache to disk in the PHDF5 case.
+ *
+ * Prior to this update, the presumption was that all metadata caches would
+ * write the same data at the same time since all operations modifying
+ * metadata must be performed collectively. Given this assumption, it was
+ * safe to allow only the writes from process 0 to actually make it to disk,
+ * while metadata writes from all other processes were discarded.
+ *
+ * Unfortunately, this presumption is in error as operations that read
+ * metadata need not be collective, but can change the location of dirty
+ * entries in the metadata cache LRU lists. This can result in the same
+ * metadata write operation triggering writes from the metadata caches on
+ * some processes, but not all (causing a hang), or in different sets of
+ * entries being written from different caches (potentially resulting in
+ * metadata corruption in the file).
+ *
+ * To deal with this issue, I decided to apply a paradigm shift to the way
+ * metadata is written to disk.
+ *
+ * With this set of changes, only the metadata cache on process 0 is able
+ * to write metadata to disk, although metadata caches on all other
+ * processes can read metadata from disk as before.
+ *
+ * To keep all the other caches from getting plugged up with dirty metadata,
+ * process 0 periodically broadcasts a list of entries that it has flushed
+ * since that last notice, and which are currently clean. The other caches
+ * mark these entries as clean as well, which allows them to evict the
+ * entries as needed.
+ *
+ * One obvious problem in this approach is synchronizing the broadcasts
+ * and receptions, as different caches may see different amounts of
+ * activity.
+ *
+ * The current solution is for the caches to track the number of bytes
+ * of newly generated dirty metadata, and to broadcast and receive
+ * whenever this value exceeds some user specified threshold.
+ *
+ * Maintaining this count is easy for all processes not on process 0 --
+ * all that is necessary is to add the size of the entry to the total
+ * whenever there is an insertion, a rename of a previously clean entry,
+ * or whever a previously clean entry is marked dirty in an unprotect.
+ *
+ * On process 0, we have to be careful not to count dirty bytes twice.
+ * If an entry is marked dirty, flushed, and marked dirty again, all
+ * within a single reporting period, it only th first marking should
+ * be added to the dirty bytes generated tally, as that is all that
+ * the other processes will see.
+ *
+ * At present, this structure exists to maintain the fields needed to
+ * implement the above scheme, and thus is only used in the parallel
+ * case. However, other uses may arise in the future.
+ *
+ * Instance of this structure are associated with metadata caches via
+ * the aux_ptr field of H5C2_t (see H5C2pkg.h). The H5AC2 code is
+ * responsible for allocating, maintaining, and discarding instances
+ * of H5AC2_aux_t.
+ *
+ * The remainder of this header comments documents the individual fields
+ * of the structure.
+ *
+ * JRM - 6/27/05
+ *
+ * magic: Unsigned 32 bit integer always set to
+ * H5AC2__H5AC2_AUX_T_MAGIC. This field is used to validate
+ * pointers to instances of H5AC2_aux_t.
+ *
+ * mpi_comm: MPI communicator associated with the file for which the
+ * cache has been created.
+ *
+ * mpi_rank: MPI rank of this process within mpi_comm.
+ *
+ * mpi_size: Number of processes in mpi_comm.
+ *
+ * write_permitted: Boolean flag used to control whether the cache
+ * is permitted to write to file.
+ *
+ * dirty_bytes_threshold: Integer field containing the dirty bytes
+ * generation threashold. Whenever dirty byte creation
+ * exceeds this value, the metadata cache on process 0
+ * broadcasts a list of the entries it has flushed since
+ * the last broadcast (or since the beginning of execution)
+ * and which are currently clean (if they are still in the
+ * cache)
+ *
+ * Similarly, metadata caches on processes other than process
+ * 0 will attempt to receive a list of clean entries whenever
+ * the threshold is exceeded.
+ *
+ * dirty_bytes: Integer field containing the number of bytes of dirty
+ * metadata generated since the beginning of the computation,
+ * or (more typically) since the last clean entries list
+ * broadcast. This field is reset to zero after each such
+ * broadcast.
+ *
+ * dirty_bytes_propagations: This field only exists when the
+ * H5AC2_DEBUG_DIRTY_BYTES_CREATION #define is TRUE.
+ *
+ * It is used to track the number of times the cleaned list
+ * has been propagated from process 0 to the other
+ * processes.
+ *
+ * unprotect_dirty_bytes: This field only exists when the
+ * H5AC2_DEBUG_DIRTY_BYTES_CREATION #define is TRUE.
+ *
+ * It is used to track the number of dirty bytes created
+ * via unprotect operations since the last time the cleaned
+ * list was propagated.
+ *
+ * unprotect_dirty_bytes_updates: This field only exists when the
+ * H5AC2_DEBUG_DIRTY_BYTES_CREATION #define is TRUE.
+ *
+ * It is used to track the number of times dirty bytes have
+ * been created via unprotect operations since the last time
+ * the cleaned list was propagated.
+ *
+ * insert_dirty_bytes: This field only exists when the
+ * H5AC2_DEBUG_DIRTY_BYTES_CREATION #define is TRUE.
+ *
+ * It is used to track the number of dirty bytes created
+ * via insert operations since the last time the cleaned
+ * list was propagated.
+ *
+ * insert_dirty_bytes_updates: This field only exists when the
+ * H5AC2_DEBUG_DIRTY_BYTES_CREATION #define is TRUE.
+ *
+ * It is used to track the number of times dirty bytes have
+ * been created via insert operations since the last time
+ * the cleaned list was propagated.
+ *
+ * rename_dirty_bytes: This field only exists when the
+ * H5AC2_DEBUG_DIRTY_BYTES_CREATION #define is TRUE.
+ *
+ * It is used to track the number of dirty bytes created
+ * via rename operations since the last time the cleaned
+ * list was propagated.
+ *
+ * rename_dirty_bytes_updates: This field only exists when the
+ * H5AC2_DEBUG_DIRTY_BYTES_CREATION #define is TRUE.
+ *
+ * It is used to track the number of times dirty bytes have
+ * been created via rename operations since the last time
+ * the cleaned list was propagated.
+ *
+ * d_slist_ptr: Pointer to an instance of H5SL_t used to maintain a list
+ * of entries that have been dirtied since the last time they
+ * were listed in a clean entries broadcast. This list is
+ * only maintained by the metadata cache on process 0 -- it
+ * it used to maintain a view of the dirty entries as seen
+ * by the other caches, so as to keep the dirty bytes count
+ * in synchronization with them.
+ *
+ * Thus on process 0, the dirty_bytes count is incremented
+ * only if either
+ *
+ * 1) an entry is inserted in the metadata cache, or
+ *
+ * 2) a previously clean entry is renamed, and it does not
+ * already appear in the dirty entry list, or
+ *
+ * 3) a previously clean entry is unprotected with the
+ * dirtied flag set and the entry does not already appear
+ * in the dirty entry list.
+ *
+ * Entries are added to the dirty entry list whever they cause
+ * the dirty bytes count to be increased. They are removed
+ * when they appear in a clean entries broadcast. Note that
+ * renames must be reflected in the dirty entry list.
+ *
+ * To reitterate, this field is only used on process 0 -- it
+ * should be NULL on all other processes.
+ *
+ * d_slist_len: Integer field containing the number of entries in the
+ * dirty entry list. This field should always contain the
+ * value 0 on all processes other than process 0. It exists
+ * primarily for sanity checking.
+ *
+ * c_slist_ptr: Pointer to an instance of H5SL_t used to maintain a list
+ * of entries that were dirty, have been flushed
+ * to disk since the last clean entries broadcast, and are
+ * still clean. Since only process 0 can write to disk, this
+ * list only exists on process 0.
+ *
+ * In essence, this slist is used to assemble the contents of
+ * the next clean entries broadcast. The list emptied after
+ * each broadcast.
+ *
+ * c_slist_len: Integer field containing the number of entries in the clean
+ * entries list (*c_slist_ptr). This field should always
+ * contain the value 0 on all processes other than process 0.
+ * It exists primarily for sanity checking.
+ *
+ * write_done: In the parallel test bed, it is necessary to ensure that
+ * all writes to the server process from cache 0 complete
+ * before it enters the barrier call with the other caches.
+ *
+ * The write_done callback allows t_cache to do this without
+ * requiring an ACK on each write. Since these ACKs greatly
+ * increase the run time on some platforms, this is a
+ * significant optimization.
+ *
+ * This field must be set to NULL when the callback is not
+ * needed.
+ *
+ ****************************************************************************/
+
+#ifdef H5_HAVE_PARALLEL
+
+#define H5AC2__H5AC2_AUX_T_MAGIC (unsigned)0x00D0A02
+
+typedef struct H5AC2_aux_t
+{
+ uint32_t magic;
+
+ MPI_Comm mpi_comm;
+
+ int mpi_rank;
+
+ int mpi_size;
+
+ hbool_t write_permitted;
+
+ int32_t dirty_bytes_threshold;
+
+ int32_t dirty_bytes;
+
+#if H5AC2_DEBUG_DIRTY_BYTES_CREATION
+
+ int32_t dirty_bytes_propagations;
+
+ int32_t unprotect_dirty_bytes;
+ int32_t unprotect_dirty_bytes_updates;
+
+ int32_t insert_dirty_bytes;
+ int32_t insert_dirty_bytes_updates;
+
+ int32_t rename_dirty_bytes;
+ int32_t rename_dirty_bytes_updates;
+
+#endif /* H5AC2_DEBUG_DIRTY_BYTES_CREATION */
+
+ H5SL_t * d_slist_ptr;
+
+ int32_t d_slist_len;
+
+ H5SL_t * c_slist_ptr;
+
+ int32_t c_slist_len;
+
+ void (* write_done)(void);
+
+} H5AC2_aux_t; /* struct H5AC2_aux_t */
+
+#endif /* H5_HAVE_PARALLEL */
+
+#endif /* _H5C2pkg_H */
diff --git a/src/H5AC2private.h b/src/H5AC2private.h
new file mode 100644
index 0000000..de76b40
--- /dev/null
+++ b/src/H5AC2private.h
@@ -0,0 +1,315 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+ * Copyright by the Board of Trustees of the University of Illinois. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5AC2private.h
+ * Jul 9 1997
+ * Robb Matzke <matzke@llnl.gov>
+ *
+ * Purpose: Constants and typedefs available to the rest of the
+ * library.
+ *
+ * Modifications: JRM - 6/4/04
+ * Complete re-write for a new caching algorithm
+ * located in H5C.c
+ *
+ * JRM - 10/18/07
+ * Copied H5ACprivate.h to H5AC2private.h and reworked
+ * to use H5C2 instead of H5C. All this is in support
+ * of cache API modifications needed for journaling.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef _H5AC2private_H
+#define _H5AC2private_H
+
+#include "H5AC2public.h" /*public prototypes */
+
+/* Pivate headers needed by this header */
+#include "H5private.h" /* Generic Functions */
+#include "H5Fprivate.h" /* File access */
+#include "H5C2private.h" /* cache */
+
+#ifdef H5_METADATA_TRACE_FILE
+#define H5AC2__TRACE_FILE_ENABLED 1
+#else /* H5_METADATA_TRACE_FILE */
+#define H5AC2__TRACE_FILE_ENABLED 0
+#endif /* H5_METADATA_TRACE_FILE */
+
+/* Types of metadata objects cached */
+typedef enum {
+ H5AC2_BT_ID = 0, /*B-tree nodes */
+ H5AC2_SNODE_ID, /*symbol table nodes */
+ H5AC2_LHEAP_ID, /*local heap */
+ H5AC2_GHEAP_ID, /*global heap */
+ H5AC2_OHDR_ID, /*object header */
+ H5AC2_BT2_HDR_ID, /*v2 B-tree header */
+ H5AC2_BT2_INT_ID, /*v2 B-tree internal node */
+ H5AC2_BT2_LEAF_ID, /*v2 B-tree leaf node */
+ H5AC2_FHEAP_HDR_ID, /*fractal heap header */
+ H5AC2_FHEAP_DBLOCK_ID,/*fractal heap direct block */
+ H5AC2_FHEAP_IBLOCK_ID,/*fractal heap indirect block */
+ H5AC2_FSPACE_HDR_ID, /*free space header */
+ H5AC2_FSPACE_SINFO_ID,/*free space sections */
+ H5AC2_SOHM_TABLE_ID, /*shared object header message master table */
+ H5AC2_SOHM_LIST_ID, /*shared message index stored as a list */
+ H5AC2_TEST_ID, /*test entry -- not used for actual files */
+ H5AC2_NTYPES /* Number of types, must be last */
+} H5AC2_type_t;
+
+/* H5AC2_DUMP_STATS_ON_CLOSE should always be FALSE when
+ * H5C2_COLLECT_CACHE_STATS is FALSE.
+ *
+ * When H5C2_COLLECT_CACHE_STATS is TRUE, H5AC2_DUMP_STATS_ON_CLOSE must
+ * be FALSE for "make check" to succeed, but may be set to TRUE at other
+ * times for debugging purposes.
+ *
+ * Hence the following, somewhat odd set of #defines.
+ */
+#if H5C2_COLLECT_CACHE_STATS
+
+#define H5AC2_DUMP_STATS_ON_CLOSE 0
+
+#else /* H5C2_COLLECT_CACHE_STATS */
+
+#define H5AC2_DUMP_STATS_ON_CLOSE 0
+
+#endif /* H5C2_COLLECT_CACHE_STATS */
+
+/* Default max metadata cache size and min clean size are give here.
+ * At present, these are the same as those given in H5C2private.h.
+ */
+
+#define H5AC2__DEFAULT_MAX_CACHE_SIZE H5C2__DEFAULT_MAX_CACHE_SIZE
+#define H5AC2__DEFAULT_MIN_CLEAN_SIZE H5C2__DEFAULT_MIN_CLEAN_SIZE
+
+
+/*
+ * Class methods pertaining to caching. Each type of cached object will
+ * have a constant variable with permanent life-span that describes how
+ * to cache the object. That variable will be of type H5AC2_class_t and
+ * have the following required fields...
+ *
+ * LOAD: Loads an object from disk to memory. The function
+ * should allocate some data structure and return it.
+ *
+ * FLUSH: Writes some data structure back to disk. It would be
+ * wise for the data structure to include dirty flags to
+ * indicate whether it really needs to be written. This
+ * function is also responsible for freeing memory allocated
+ * by the LOAD method if the DEST argument is non-zero (by
+ * calling the DEST method).
+ *
+ * DEST: Just frees memory allocated by the LOAD method.
+ *
+ * CLEAR: Just marks object as non-dirty.
+ *
+ * SIZE: Report the size (on disk) of the specified cache object.
+ * Note that the space allocated on disk may not be contiguous.
+ */
+
+#define H5AC2__SERIALIZE_RESIZED_FLAG H5C2__SERIALIZE_RESIZED_FLAG
+#define H5AC2__SERIALIZE_RENAMED_FLAG H5C2__SERIALIZE_RENAMED_FLAG
+
+typedef H5C2_deserialize_func_t H5AC2_deserialize_func_t;
+typedef H5C2_image_len_func_t H5AC2_image_len_func_t;
+typedef H5C2_serialize_func_t H5AC2_serialize_func_t;
+typedef H5C2_free_icr_func_t H5CA2_free_icr_func_t;
+typedef H5C2_clear_dirty_bits_func_t H5AC2_clear_dirty_bits_func_t;
+
+typedef H5C2_class_t H5AC2_class_t;
+
+
+
+typedef H5C2_cache_entry_t H5AC2_info_t;
+
+
+/*===----------------------------------------------------------------------===
+ * Protect Types
+ *===----------------------------------------------------------------------===
+ *
+ * These are for the wrapper functions to H5AC2_protect. They specify what
+ * type of operation you're planning on doing to the metadata. The
+ * Flexible Parallel HDF5 locking can then act accordingly.
+ */
+
+typedef enum H5AC2_protect_t {
+ H5AC2_WRITE, /* Protect object for writing */
+ H5AC2_READ /* Protect object for reading */
+} H5AC2_protect_t;
+
+
+/* Typedef for metadata cache (defined in H5C2pkg.h) */
+typedef H5C2_t H5AC2_t;
+
+/* Metadata specific properties for FAPL */
+/* (Only used for parallel I/O) */
+#ifdef H5_HAVE_PARALLEL
+/* Definitions for "block before metadata write" property */
+#define H5AC2_BLOCK_BEFORE_META_WRITE_NAME "H5AC2_block_before_meta_write"
+#define H5AC2_BLOCK_BEFORE_META_WRITE_SIZE sizeof(unsigned)
+#define H5AC2_BLOCK_BEFORE_META_WRITE_DEF 0
+
+/* Definitions for "library internal" property */
+#define H5AC2_LIBRARY_INTERNAL_NAME "H5AC2_library_internal"
+#define H5AC2_LIBRARY_INTERNAL_SIZE sizeof(unsigned)
+#define H5AC2_LIBRARY_INTERNAL_DEF 0
+#endif /* H5_HAVE_PARALLEL */
+
+/* Dataset transfer property list for flush calls */
+/* (Collective set, "block before metadata write" set and "library internal" set) */
+/* (Global variable declaration, definition is in H5AC2.c) */
+extern hid_t H5AC2_dxpl_id;
+
+/* Dataset transfer property list for independent metadata I/O calls */
+/* (just "library internal" set - i.e. independent transfer mode) */
+/* (Global variable declaration, definition is in H5AC2.c) */
+extern hid_t H5AC2_ind_dxpl_id;
+
+
+/* Default cache configuration. */
+
+#define H5AC2__DEFAULT_CACHE_CONFIG \
+{ \
+ /* int version = */ H5C2__CURR_AUTO_SIZE_CTL_VER, \
+ /* hbool_t rpt_fcn_enabled = */ FALSE, \
+ /* hbool_t open_trace_file = */ FALSE, \
+ /* hbool_t close_trace_file = */ FALSE, \
+ /* char trace_file_name[] = */ "", \
+ /* hbool_t evictions_enabled = */ TRUE, \
+ /* hbool_t set_initial_size = */ TRUE, \
+ /* size_t initial_size = */ ( 1 * 1024 * 1024 ), \
+ /* double min_clean_fraction = */ 0.5, \
+ /* size_t max_size = */ (16 * 1024 * 1024 ), \
+ /* size_t min_size = */ ( 1 * 1024 * 1024 ), \
+ /* long int epoch_length = */ 50000, \
+ /* enum H5C2_cache_incr_mode incr_mode = */ H5C2_incr__threshold, \
+ /* double lower_hr_threshold = */ 0.9, \
+ /* double increment = */ 2.0, \
+ /* hbool_t apply_max_increment = */ TRUE, \
+ /* size_t max_increment = */ (4 * 1024 * 1024), \
+ /* enum H5C2_cache_decr_mode decr_mode = */ \
+ H5C2_decr__age_out_with_threshold, \
+ /* double upper_hr_threshold = */ 0.999, \
+ /* double decrement = */ 0.9, \
+ /* hbool_t apply_max_decrement = */ TRUE, \
+ /* size_t max_decrement = */ (1 * 1024 * 1024), \
+ /* int epochs_before_eviction = */ 3, \
+ /* hbool_t apply_empty_reserve = */ TRUE, \
+ /* double empty_reserve = */ 0.1, \
+ /* int dirty_bytes_threshold = */ (256 * 1024) \
+}
+
+
+/*
+ * Library prototypes.
+ */
+
+/* #defines of flags used in the flags parameters in some of the
+ * following function calls. Note that they are just copies of
+ * the equivalent flags from H5C2private.h.
+ */
+
+#define H5AC2__NO_FLAGS_SET H5C2__NO_FLAGS_SET
+#define H5AC2__SET_FLUSH_MARKER_FLAG H5C2__SET_FLUSH_MARKER_FLAG
+#define H5AC2__DELETED_FLAG H5C2__DELETED_FLAG
+#define H5AC2__DIRTIED_FLAG H5C2__DIRTIED_FLAG
+#define H5AC2__SIZE_CHANGED_FLAG H5C2__SIZE_CHANGED_FLAG
+#define H5AC2__PIN_ENTRY_FLAG H5C2__PIN_ENTRY_FLAG
+#define H5AC2__UNPIN_ENTRY_FLAG H5C2__UNPIN_ENTRY_FLAG
+#define H5AC2__FLUSH_INVALIDATE_FLAG H5C2__FLUSH_INVALIDATE_FLAG
+#define H5AC2__FLUSH_CLEAR_ONLY_FLAG H5C2__FLUSH_CLEAR_ONLY_FLAG
+#define H5AC2__FLUSH_MARKED_ENTRIES_FLAG H5C2__FLUSH_MARKED_ENTRIES_FLAG
+#define H5AC2__FLUSH_IGNORE_PROTECTED_FLAG H5C2__FLUSH_IGNORE_PROTECTED_FLAG
+
+
+/* #defines of flags used to report entry status in the
+ * H5AC2_get_entry_status() call.
+ */
+
+#define H5AC2_ES__IN_CACHE 0x0001
+#define H5AC2_ES__IS_DIRTY 0x0002
+#define H5AC2_ES__IS_PROTECTED 0x0004
+#define H5AC2_ES__IS_PINNED 0x0008
+
+
+/* external function declarations: */
+
+H5_DLL herr_t H5AC2_init(void);
+H5_DLL herr_t H5AC2_create(const H5F_t *f, H5AC2_cache_config_t *config_ptr);
+H5_DLL herr_t H5AC2_get_entry_status(H5F_t * f, haddr_t addr,
+ unsigned * status_ptr);
+H5_DLL herr_t H5AC2_set(H5F_t *f, hid_t dxpl_id, const H5AC2_class_t *type,
+ haddr_t addr, size_t len, void *thing,
+ unsigned int flags);
+H5_DLL herr_t H5AC2_pin_protected_entry(H5F_t * f, void * thing);
+H5_DLL void * H5AC2_protect(H5F_t *f, hid_t dxpl_id, const H5AC2_class_t *type,
+ haddr_t addr, size_t len, const void *udata,
+ H5AC2_protect_t rw);
+H5_DLL herr_t H5AC2_resize_pinned_entry(H5F_t * f,
+ void * thing,
+ size_t new_size);
+H5_DLL herr_t H5AC2_unpin_entry(H5F_t * f,
+ void * thing);
+H5_DLL herr_t H5AC2_unprotect(H5F_t *f, hid_t dxpl_id,
+ const H5AC2_class_t *type, haddr_t addr,
+ size_t new_size, void *thing, unsigned flags);
+H5_DLL herr_t H5AC2_flush(H5F_t *f, hid_t dxpl_id, unsigned flags);
+H5_DLL herr_t H5AC2_mark_pinned_entry_dirty(H5F_t * f,
+ void * thing,
+ hbool_t size_changed,
+ size_t new_size);
+H5_DLL herr_t H5AC2_mark_pinned_or_protected_entry_dirty(H5F_t * f,
+ void * thing);
+H5_DLL herr_t H5AC2_rename(H5F_t *f, const H5AC2_class_t *type,
+ haddr_t old_addr, haddr_t new_addr);
+
+H5_DLL herr_t H5AC2_dest(H5F_t *f, hid_t dxpl_id);
+
+H5_DLL herr_t H5AC2_expunge_entry(H5F_t *f, hid_t dxpl_id,
+ const H5AC2_class_t *type, haddr_t addr);
+
+H5_DLL herr_t H5AC2_set_write_done_callback(H5C2_t * cache_ptr,
+ void (* write_done)(void));
+H5_DLL herr_t H5AC2_stats(const H5F_t *f);
+
+H5_DLL herr_t H5AC2_get_cache_auto_resize_config(H5AC2_t * cache_ptr,
+ H5AC2_cache_config_t *config_ptr);
+
+H5_DLL herr_t H5AC2_get_cache_size(H5AC2_t * cache_ptr,
+ size_t * max_size_ptr,
+ size_t * min_clean_size_ptr,
+ size_t * cur_size_ptr,
+ int32_t * cur_num_entries_ptr);
+
+H5_DLL herr_t H5AC2_get_cache_hit_rate(H5AC2_t * cache_ptr,
+ double * hit_rate_ptr);
+
+H5_DLL herr_t H5AC2_reset_cache_hit_rate_stats(H5AC2_t * cache_ptr);
+
+H5_DLL herr_t H5AC2_set_cache_auto_resize_config(H5AC2_t * cache_ptr,
+ H5AC2_cache_config_t *config_ptr);
+
+H5_DLL herr_t H5AC2_validate_config(H5AC2_cache_config_t * config_ptr);
+
+H5_DLL herr_t H5AC2_close_trace_file(H5AC2_t * cache_ptr);
+
+H5_DLL herr_t H5AC2_open_trace_file(H5AC2_t * cache_ptr,
+ const char * trace_file_name);
+
+#endif /* !_H5AC2private_H */
+
diff --git a/src/H5AC2public.h b/src/H5AC2public.h
new file mode 100644
index 0000000..3c88ef1
--- /dev/null
+++ b/src/H5AC2public.h
@@ -0,0 +1,393 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+ * Copyright by the Board of Trustees of the University of Illinois. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5ACpublic.h
+ * Jul 10 1997
+ * Robb Matzke <matzke@llnl.gov>
+ *
+ * Purpose: Public include file for cache functions.
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _H5AC2public_H
+#define _H5AC2public_H
+
+/* Public headers needed by this file */
+#include "H5public.h"
+#include "H5C2public.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define H5AC2__MAX_TRACE_FILE_NAME_LEN 1024
+
+/****************************************************************************
+ *
+ * structure H5AC2_cache_config_t
+ *
+ * H5AC2_cache_config_t is a public structure intended for use in public APIs.
+ * At least in its initial incarnation, it is basicaly a copy of struct
+ * H5C2_auto_size_ctl_t, minus the report_fcn field, and plus the
+ * dirty_bytes_threshold field.
+ *
+ * The report_fcn field is omitted, as including it would require us to
+ * make H5C2_t structure public.
+ *
+ * The dirty_bytes_threshold field does not appear in H5C2_auto_size_ctl_t,
+ * as synchronization between caches on different processes is handled at
+ * the H5AC2 level, not at the level of H5C2. Note however that there is
+ * considerable interaction between this value and the other fields in this
+ * structure.
+ *
+ * Similarly, the open_trace_file, close_trace_file, and trace_file_name
+ * fields do not appear in H5C2_auto_size_ctl_t, as most trace file
+ * issues are handled at the H5AC2 level. The one exception is storage of
+ * the pointer to the trace file, which is handled by H5C2.
+ *
+ * The structure is in H5AC2public.h as we may wish to allow different
+ * configuration options for metadata and raw data caches.
+ *
+ * The fields of the structure are discussed individually below:
+ *
+ * version: Integer field containing the version number of this version
+ * of the H5AC2_cache_config_t structure. Any instance of
+ * H5AC2_cache_config_t passed to the cache must have a known
+ * version number, or an error will be flagged.
+ *
+ * rpt_fcn_enabled: Boolean field used to enable and disable the default
+ * reporting function. This function is invoked every time the
+ * automatic cache resize code is run, and reports on its activities.
+ *
+ * This is a debugging function, and should normally be turned off.
+ *
+ * open_trace_file: Boolean field indicating whether the trace_file_name
+ * field should be used to open a trace file for the cache.
+ *
+ * The trace file is a debuging feature that allow the capture of
+ * top level metadata cache requests for purposes of debugging and/or
+ * optimization. This field should normally be set to FALSE, as
+ * trace file collection imposes considerable overhead.
+ *
+ * This field should only be set to TRUE when the trace_file_name
+ * contains the full path of the desired trace file, and either
+ * there is no open trace file on the cache, or the close_trace_file
+ * field is also TRUE.
+ *
+ * close_trace_file: Boolean field indicating whether the current trace
+ * file (if any) should be closed.
+ *
+ * See the above comments on the open_trace_file field. This field
+ * should be set to FALSE unless there is an open trace file on the
+ * cache that you wish to close.
+ *
+ * trace_file_name: Full path of the trace file to be opened if the
+ * open_trace_file field is TRUE.
+ *
+ * In the parallel case, an ascii representation of the mpi rank of
+ * the process will be appended to the file name to yield a unique
+ * trace file name for each process.
+ *
+ * The length of the path must not exceed H5AC2__MAX_TRACE_FILE_NAME_LEN
+ * characters.
+ *
+ * evictions_enabled: Boolean field used to either report the current
+ * evictions enabled status of the cache, or to set the cache's
+ * evictions enabled status.
+ *
+ * In general, the metadata cache should always be allowed to
+ * evict entries. However, in some cases it is advantageous to
+ * disable evictions briefly, and thereby postpone metadata
+ * writes. However, this must be done with care, as the cache
+ * can grow quickly. If you do this, re-enable evictions as
+ * soon as possible and monitor cache size.
+ *
+ * At present, evictions can only be disabled if automatic
+ * cache resizing is also disabled (that is, ( incr_mode ==
+ * H5C2_incr__off ) && ( decr_mode == H5C2_decr__off )). There
+ * is no logical reason why this should be so, but it simplifies
+ * implementation and testing, and I can't think of any reason
+ * why it would be desireable. If you can think of one, I'll
+ * revisit the issue.
+ *
+ * set_initial_size: Boolean flag indicating whether the size of the
+ * initial size of the cache is to be set to the value given in
+ * the initial_size field. If set_initial_size is FALSE, the
+ * initial_size field is ignored.
+ *
+ * initial_size: If enabled, this field contain the size the cache is
+ * to be set to upon receipt of this structure. Needless to say,
+ * initial_size must lie in the closed interval [min_size, max_size].
+ *
+ * min_clean_fraction: double in the range 0 to 1 indicating the fraction
+ * of the cache that is to be kept clean. This field is only used
+ * in parallel mode. Typical values are 0.1 to 0.5.
+ *
+ * max_size: Maximum size to which the cache can be adjusted. The
+ * supplied value must fall in the closed interval
+ * [MIN_MAX_CACHE_SIZE, MAX_MAX_CACHE_SIZE]. Also, max_size must
+ * be greater than or equal to min_size.
+ *
+ * min_size: Minimum size to which the cache can be adjusted. The
+ * supplied value must fall in the closed interval
+ * [H5C2__MIN_MAX_CACHE_SIZE, H5C2__MAX_MAX_CACHE_SIZE]. Also, min_size
+ * must be less than or equal to max_size.
+ *
+ * epoch_length: Number of accesses on the cache over which to collect
+ * hit rate stats before running the automatic cache resize code,
+ * if it is enabled.
+ *
+ * At the end of an epoch, we discard prior hit rate data and start
+ * collecting afresh. The epoch_length must lie in the closed
+ * interval [H5C2__MIN_AR_EPOCH_LENGTH, H5C2__MAX_AR_EPOCH_LENGTH].
+ *
+ *
+ * Cache size increase control fields:
+ *
+ * incr_mode: Instance of the H5C2_cache_incr_mode enumerated type whose
+ * value indicates how we determine whether the cache size should be
+ * increased. At present there are two possible values:
+ *
+ * H5C2_incr__off: Don't attempt to increase the size of the cache
+ * automatically.
+ *
+ * When this increment mode is selected, the remaining fields
+ * in the cache size increase section ar ignored.
+ *
+ * H5C2_incr__threshold: Attempt to increase the size of the cache
+ * whenever the average hit rate over the last epoch drops
+ * below the value supplied in the lower_hr_threshold
+ * field.
+ *
+ * Note that this attempt will fail if the cache is already
+ * at its maximum size, or if the cache is not already using
+ * all available space.
+ *
+ * Note that you must set decr_mode to H5C2_incr__off if you
+ * disable metadata cache entry evictions.
+ *
+ * lower_hr_threshold: Lower hit rate threshold. If the increment mode
+ * (incr_mode) is H5C2_incr__threshold and the hit rate drops below the
+ * value supplied in this field in an epoch, increment the cache size by
+ * size_increment. Note that cache size may not be incremented above
+ * max_size, and that the increment may be further restricted by the
+ * max_increment field if it is enabled.
+ *
+ * When enabled, this field must contain a value in the range [0.0, 1.0].
+ * Depending on the incr_mode selected, it may also have to be less than
+ * upper_hr_threshold.
+ *
+ * increment: Double containing the multiplier used to derive the new
+ * cache size from the old if a cache size increment is triggered.
+ * The increment must be greater than 1.0, and should not exceed 2.0.
+ *
+ * The new cache size is obtained my multiplying the current max cache
+ * size by the increment, and then clamping to max_size and to stay
+ * within the max_increment as necessary.
+ *
+ * apply_max_increment: Boolean flag indicating whether the max_increment
+ * field should be used to limit the maximum cache size increment.
+ *
+ * max_increment: If enabled by the apply_max_increment field described
+ * above, this field contains the maximum number of bytes by which the
+ * cache size can be increased in a single re-size.
+ *
+ *
+ * Cache size decrease control fields:
+ *
+ * decr_mode: Instance of the H5C2_cache_decr_mode enumerated type whose
+ * value indicates how we determine whether the cache size should be
+ * decreased. At present there are four possibilities.
+ *
+ * H5C2_decr__off: Don't attempt to decrease the size of the cache
+ * automatically.
+ *
+ * When this increment mode is selected, the remaining fields
+ * in the cache size decrease section are ignored.
+ *
+ * H5C2_decr__threshold: Attempt to decrease the size of the cache
+ * whenever the average hit rate over the last epoch rises
+ * above the value supplied in the upper_hr_threshold
+ * field.
+ *
+ * H5C2_decr__age_out: At the end of each epoch, search the cache for
+ * entries that have not been accessed for at least the number
+ * of epochs specified in the epochs_before_eviction field, and
+ * evict these entries. Conceptually, the maximum cache size
+ * is then decreased to match the new actual cache size. However,
+ * this reduction may be modified by the min_size, the
+ * max_decrement, and/or the empty_reserve.
+ *
+ * H5C2_decr__age_out_with_threshold: Same as age_out, but we only
+ * attempt to reduce the cache size when the hit rate observed
+ * over the last epoch exceeds the value provided in the
+ * upper_hr_threshold field.
+ *
+ * Note that you must set decr_mode to H5C2_decr__off if you
+ * disable metadata cache entry evictions.
+ *
+ * upper_hr_threshold: Upper hit rate threshold. The use of this field
+ * varies according to the current decr_mode:
+ *
+ * H5C2_decr__off or H5C2_decr__age_out: The value of this field is
+ * ignored.
+ *
+ * H5C2_decr__threshold: If the hit rate exceeds this threshold in any
+ * epoch, attempt to decrement the cache size by size_decrement.
+ *
+ * Note that cache size may not be decremented below min_size.
+ *
+ * Note also that if the upper_threshold is 1.0, the cache size
+ * will never be reduced.
+ *
+ * H5C2_decr__age_out_with_threshold: If the hit rate exceeds this
+ * threshold in any epoch, attempt to reduce the cache size
+ * by evicting entries that have not been accessed for more
+ * than the specified number of epochs.
+ *
+ * decrement: This field is only used when the decr_mode is
+ * H5C2_decr__threshold.
+ *
+ * The field is a double containing the multiplier used to derive the
+ * new cache size from the old if a cache size decrement is triggered.
+ * The decrement must be in the range 0.0 (in which case the cache will
+ * try to contract to its minimum size) to 1.0 (in which case the
+ * cache will never shrink).
+ *
+ * apply_max_decrement: Boolean flag used to determine whether decrements
+ * in cache size are to be limited by the max_decrement field.
+ *
+ * max_decrement: Maximum number of bytes by which the cache size can be
+ * decreased in a single re-size. Note that decrements may also be
+ * restricted by the min_size of the cache, and (in age out modes) by
+ * the empty_reserve field.
+ *
+ * epochs_before_eviction: Integer field used in H5C2_decr__age_out and
+ * H5C2_decr__age_out_with_threshold decrement modes.
+ *
+ * This field contains the number of epochs an entry must remain
+ * unaccessed before it is evicted in an attempt to reduce the
+ * cache size. If applicable, this field must lie in the range
+ * [1, H5C2__MAX_EPOCH_MARKERS].
+ *
+ * apply_empty_reserve: Boolean field controlling whether the empty_reserve
+ * field is to be used in computing the new cache size when the
+ * decr_mode is H5C2_decr__age_out or H5C2_decr__age_out_with_threshold.
+ *
+ * empty_reserve: To avoid a constant racheting down of cache size by small
+ * amounts in the H5C2_decr__age_out and H5C2_decr__age_out_with_threshold
+ * modes, this field allows one to require that any cache size
+ * reductions leave the specified fraction of unused space in the cache.
+ *
+ * The value of this field must be in the range [0.0, 1.0]. I would
+ * expect typical values to be in the range of 0.01 to 0.1.
+ *
+ *
+ * Parallel Configuration Fields:
+ *
+ * In PHDF5, all operations that modify metadata must be executed collectively.
+ * We used to think that this was enough to ensure consistency across the
+ * metadata caches, but since we allow processes to read metadata individually,
+ * the order of dirty entries in the LRU list can vary across processes,
+ * which can result in inconsistencies between the caches.
+ *
+ * To prevent this, only the metadata cache on process 0 is allowed to write
+ * to file, and then only after synchronizing with the other caches. After
+ * it writes entries to file, it sends the base addresses of the now clean
+ * entries to the other caches, so they can mark these entries clean as well.
+ *
+ * The different caches know when to synchronize caches by counting the
+ * number of bytes of dirty metadata created by the collective operations
+ * modifying metadata. Whenever this count exceeds a user specified
+ * threshold (see below), process 0 flushes down to its minimum clean size,
+ * and then sends the list of newly cleaned entries to the other caches.
+ *
+ * dirty_bytes_threshold: Threshold of dirty byte creation used to
+ * synchronize updates between caches. (See above for outline and
+ * motivation.)
+ *
+ * This value MUST be consistant across all processes accessing the
+ * file. This field is ignored unless HDF5 has been compiled for
+ * parallel.
+ *
+ ****************************************************************************/
+
+#define H5AC2__CURR_CACHE_CONFIG_VERSION 1
+
+typedef struct H5AC2_cache_config_t
+{
+ /* general configuration fields: */
+ int version;
+
+ hbool_t rpt_fcn_enabled;
+
+ hbool_t open_trace_file;
+ hbool_t close_trace_file;
+ char trace_file_name[H5AC2__MAX_TRACE_FILE_NAME_LEN+1];
+
+ hbool_t evictions_enabled;
+
+ hbool_t set_initial_size;
+ size_t initial_size;
+
+ double min_clean_fraction;
+
+ size_t max_size;
+ size_t min_size;
+
+ long int epoch_length;
+
+
+ /* size increase control fields: */
+ enum H5C2_cache_incr_mode incr_mode;
+
+ double lower_hr_threshold;
+
+ double increment;
+
+ hbool_t apply_max_increment;
+ size_t max_increment;
+
+
+ /* size decrease control fields: */
+ enum H5C2_cache_decr_mode decr_mode;
+
+ double upper_hr_threshold;
+
+ double decrement;
+
+ hbool_t apply_max_decrement;
+ size_t max_decrement;
+
+ int epochs_before_eviction;
+
+ hbool_t apply_empty_reserve;
+ double empty_reserve;
+
+
+ /* parallel configuration fields: */
+ int dirty_bytes_threshold;
+
+} H5AC2_cache_config_t;
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/H5C2.c b/src/H5C2.c
new file mode 100644
index 0000000..373191f
--- /dev/null
+++ b/src/H5C2.c
@@ -0,0 +1,11024 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+ * Copyright by the Board of Trustees of the University of Illinois. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5C2.c
+ * June 1 2004
+ * John Mainzer
+ *
+ * Purpose: Functions in this file implement a generic cache for
+ * things which exist on disk, and which may be
+ * unambiguously referenced by their disk addresses.
+ *
+ * The code in this module was initially written in
+ * support of a complete re-write of the metadata cache
+ * in H5AC.c However, other uses for the cache code
+ * suggested themselves, and thus this file was created
+ * in an attempt to support re-use.
+ *
+ * For a detailed overview of the cache, please see the
+ * header comment for H5C2_t in H5C2pkg.h.
+ *
+ * Modifications:
+ *
+ * QAK - 11/27/2004
+ * Switched over to using skip list routines instead of TBBT
+ * routines.
+ *
+ * JRM - 12/15/04
+ * Added code supporting manual and automatic cache resizing.
+ * See the header for H5C2_auto_size_ctl_t in H5C2private.h for
+ * an overview.
+ *
+ * Some elements of the automatic cache resize code depend on
+ * the LRU list. Thus if we ever choose to support a new
+ * replacement policy, we will either have to disable those
+ * elements of the auto resize code when running the new
+ * policy, or modify them to make use of similar information
+ * maintained by the new policy code.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/**************************************************************************
+ *
+ * To Do:
+ *
+ * Code Changes:
+ *
+ * - Remove extra functionality in H5C2_flush_single_entry()?
+ *
+ * - Change protect/unprotect to lock/unlock.
+ *
+ * - Change the way the dirty flag is set. Probably pass it in
+ * as a parameter in unprotect & insert.
+ *
+ * - Size should also be passed in as a parameter in insert and
+ * unprotect -- or some other way should be found to advise the
+ * cache of changes in entry size.
+ *
+ * - Flush entries in increasing address order in
+ * H5C2_make_space_in_cache().
+ *
+ * - Also in H5C2_make_space_in_cache(), use high and low water marks
+ * to reduce the number of I/O calls.
+ *
+ * - When flushing, attempt to combine contiguous entries to reduce
+ * I/O overhead. Can't do this just yet as some entries are not
+ * contiguous. Do this in parallel only or in serial as well?
+ *
+ * - Create MPI type for dirty objects when flushing in parallel.
+ *
+ * - Now that TBBT routines aren't used, fix nodes in memory to
+ * point directly to the skip list node from the LRU list, eliminating
+ * skip list lookups when evicting objects from the cache.
+ *
+ * Tests:
+ *
+ * - Trim execution time. (This is no longer a major issue with the
+ * shift from the TBBT to a hash table for indexing.)
+ *
+ * - Add random tests.
+ *
+ **************************************************************************/
+
+#define H5C2_PACKAGE /*suppress error about including H5C2pkg */
+#define H5F_PACKAGE /*suppress error about including H5Fpkg */
+
+
+#include "H5private.h" /* Generic Functions */
+#include "H5C2pkg.h" /* Cache */
+#include "H5Dprivate.h" /* Dataset functions */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5Fpkg.h" /* Files */
+#include "H5FDprivate.h" /* File drivers */
+#include "H5FLprivate.h" /* Free Lists */
+#include "H5Iprivate.h" /* IDs */
+#include "H5MMprivate.h" /* Memory management */
+#include "H5Pprivate.h" /* Property lists */
+#include "H5SLprivate.h" /* Skip lists */
+
+
+/****************************************************************************
+ *
+ * We maintain doubly linked lists of instances of H5C2_cache_entry_t for a
+ * variety of reasons -- protected list, LRU list, and the clean and dirty
+ * LRU lists at present. The following macros support linking and unlinking
+ * of instances of H5C2_cache_entry_t by both their regular and auxilary next
+ * and previous pointers.
+ *
+ * The size and length fields are also maintained.
+ *
+ * Note that the relevant pair of prev and next pointers are presumed to be
+ * NULL on entry in the insertion macros.
+ *
+ * Finally, observe that the sanity checking macros evaluate to the empty
+ * string when H5C2_DO_SANITY_CHECKS is FALSE. They also contain calls
+ * to the HGOTO_ERROR macro, which may not be appropriate in all cases.
+ * If so, we will need versions of the insertion and deletion macros which
+ * do not reference the sanity checking macros.
+ * JRM - 5/5/04
+ *
+ * Changes:
+ *
+ * - Removed the line:
+ *
+ * ( ( (Size) == (entry_ptr)->size ) && ( (len) != 1 ) ) ||
+ *
+ * from the H5C2__DLL_PRE_REMOVE_SC macro. With the addition of the
+ * epoch markers used in the age out based cache size reduction algorithm,
+ * this invarient need not hold, as the epoch markers are of size 0.
+ *
+ * One could argue that I should have given the epoch markers a positive
+ * size, but this would break the index_size = LRU_list_size + pl_size
+ * + pel_size invarient.
+ *
+ * Alternatively, I could pass the current decr_mode in to the macro,
+ * and just skip the check whenever epoch markers may be in use.
+ *
+ * However, any size errors should be caught when the cache is flushed
+ * and destroyed. Until we are tracking such an error, this should be
+ * good enough.
+ * JRM - 12/9/04
+ *
+ *
+ * - In the H5C2__DLL_PRE_INSERT_SC macro, replaced the lines:
+ *
+ * ( ( (len) == 1 ) &&
+ * ( ( (head_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) ||
+ * ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) )
+ * )
+ * ) ||
+ *
+ * with:
+ *
+ * ( ( (len) == 1 ) &&
+ * ( ( (head_ptr) != (tail_ptr) ) ||
+ * ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) )
+ * )
+ * ) ||
+ *
+ * Epoch markers have size 0, so we can now have a non-empty list with
+ * zero size. Hence the "( (Size) <= 0 )" clause cause false failures
+ * in the sanity check. Since "Size" is typically a size_t, it can't
+ * take on negative values, and thus the revised clause "( (Size) < 0 )"
+ * caused compiler warnings.
+ * JRM - 12/22/04
+ *
+ * - In the H5C2__DLL_SC macro, replaced the lines:
+ *
+ * ( ( (len) == 1 ) &&
+ * ( ( (head_ptr) != (tail_ptr) ) || ( (cache_ptr)->size <= 0 ) ||
+ * ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) )
+ * )
+ * ) ||
+ *
+ * with
+ *
+ * ( ( (len) == 1 ) &&
+ * ( ( (head_ptr) != (tail_ptr) ) ||
+ * ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) )
+ * )
+ * ) ||
+ *
+ * Epoch markers have size 0, so we can now have a non-empty list with
+ * zero size. Hence the "( (Size) <= 0 )" clause cause false failures
+ * in the sanity check. Since "Size" is typically a size_t, it can't
+ * take on negative values, and thus the revised clause "( (Size) < 0 )"
+ * caused compiler warnings.
+ * JRM - 1/10/05
+ *
+ * - Added the H5C2__DLL_UPDATE_FOR_SIZE_CHANGE macro and the associated
+ * sanity checking macros. These macro are used to update the size of
+ * a DLL when one of its entries changes size.
+ *
+ * JRM - 9/8/05
+ *
+ ****************************************************************************/
+
+#if H5C2_DO_SANITY_CHECKS
+
+#define H5C2__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+if ( ( (head_ptr) == NULL ) || \
+ ( (tail_ptr) == NULL ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (len) <= 0 ) || \
+ ( (Size) < (entry_ptr)->size ) || \
+ ( ( (entry_ptr)->prev == NULL ) && ( (head_ptr) != (entry_ptr) ) ) || \
+ ( ( (entry_ptr)->next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \
+ ( ( (len) == 1 ) && \
+ ( ! ( ( (head_ptr) == (entry_ptr) ) && \
+ ( (tail_ptr) == (entry_ptr) ) && \
+ ( (entry_ptr)->next == NULL ) && \
+ ( (entry_ptr)->prev == NULL ) && \
+ ( (Size) == (entry_ptr)->size ) \
+ ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "DLL pre remove SC failed") \
+}
+
+#define H5C2__DLL_SC(head_ptr, tail_ptr, len, Size, fv) \
+if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
+ ( (head_ptr) != (tail_ptr) ) \
+ ) || \
+ ( (len) < 0 ) || \
+ ( (Size) < 0 ) || \
+ ( ( (len) == 1 ) && \
+ ( ( (head_ptr) != (tail_ptr) ) || \
+ ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \
+ ) \
+ ) || \
+ ( ( (len) >= 1 ) && \
+ ( ( (head_ptr) == NULL ) || ( (head_ptr)->prev != NULL ) || \
+ ( (tail_ptr) == NULL ) || ( (tail_ptr)->next != NULL ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "DLL sanity check failed") \
+}
+
+#define H5C2__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+if ( ( (entry_ptr) == NULL ) || \
+ ( (entry_ptr)->next != NULL ) || \
+ ( (entry_ptr)->prev != NULL ) || \
+ ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
+ ( (head_ptr) != (tail_ptr) ) \
+ ) || \
+ ( (len) < 0 ) || \
+ ( ( (len) == 1 ) && \
+ ( ( (head_ptr) != (tail_ptr) ) || \
+ ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \
+ ) \
+ ) || \
+ ( ( (len) >= 1 ) && \
+ ( ( (head_ptr) == NULL ) || ( (head_ptr)->prev != NULL ) || \
+ ( (tail_ptr) == NULL ) || ( (tail_ptr)->next != NULL ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "DLL pre insert SC failed") \
+}
+
+#define H5C2__DLL_PRE_SIZE_UPDATE_SC(dll_len, dll_size, old_size, new_size) \
+if ( ( (dll_len) <= 0 ) || \
+ ( (dll_size) <= 0 ) || \
+ ( (old_size) <= 0 ) || \
+ ( (old_size) > (dll_size) ) || \
+ ( (new_size) <= 0 ) || \
+ ( ( (dll_len) == 1 ) && ( (old_size) != (dll_size) ) ) ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "DLL pre size update SC failed") \
+}
+
+#define H5C2__DLL_POST_SIZE_UPDATE_SC(dll_len, dll_size, old_size, new_size) \
+if ( ( (new_size) > (dll_size) ) || \
+ ( ( (dll_len) == 1 ) && ( (new_size) != (dll_size) ) ) ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "DLL post size update SC failed") \
+}
+
+#else /* H5C2_DO_SANITY_CHECKS */
+
+#define H5C2__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv)
+#define H5C2__DLL_SC(head_ptr, tail_ptr, len, Size, fv)
+#define H5C2__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv)
+#define H5C2__DLL_PRE_SIZE_UPDATE_SC(dll_len, dll_size, old_size, new_size)
+#define H5C2__DLL_POST_SIZE_UPDATE_SC(dll_len, dll_size, old_size, new_size)
+
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+
+#define H5C2__DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \
+ H5C2__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ if ( (head_ptr) == NULL ) \
+ { \
+ (head_ptr) = (entry_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ else \
+ { \
+ (tail_ptr)->next = (entry_ptr); \
+ (entry_ptr)->prev = (tail_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ (len)++; \
+ (Size) += (entry_ptr)->size;
+
+#define H5C2__DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \
+ H5C2__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ if ( (head_ptr) == NULL ) \
+ { \
+ (head_ptr) = (entry_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ else \
+ { \
+ (head_ptr)->prev = (entry_ptr); \
+ (entry_ptr)->next = (head_ptr); \
+ (head_ptr) = (entry_ptr); \
+ } \
+ (len)++; \
+ (Size) += entry_ptr->size;
+
+#define H5C2__DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \
+ H5C2__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ { \
+ if ( (head_ptr) == (entry_ptr) ) \
+ { \
+ (head_ptr) = (entry_ptr)->next; \
+ if ( (head_ptr) != NULL ) \
+ { \
+ (head_ptr)->prev = NULL; \
+ } \
+ } \
+ else \
+ { \
+ (entry_ptr)->prev->next = (entry_ptr)->next; \
+ } \
+ if ( (tail_ptr) == (entry_ptr) ) \
+ { \
+ (tail_ptr) = (entry_ptr)->prev; \
+ if ( (tail_ptr) != NULL ) \
+ { \
+ (tail_ptr)->next = NULL; \
+ } \
+ } \
+ else \
+ { \
+ (entry_ptr)->next->prev = (entry_ptr)->prev; \
+ } \
+ entry_ptr->next = NULL; \
+ entry_ptr->prev = NULL; \
+ (len)--; \
+ (Size) -= entry_ptr->size; \
+ }
+
+#define H5C2__DLL_UPDATE_FOR_SIZE_CHANGE(dll_len, dll_size, old_size, new_size) \
+ H5C2__DLL_PRE_SIZE_UPDATE_SC(dll_len, dll_size, old_size, new_size) \
+ (dll_size) -= (old_size); \
+ (dll_size) += (new_size); \
+ H5C2__DLL_POST_SIZE_UPDATE_SC(dll_len, dll_size, old_size, new_size)
+
+#if H5C2_DO_SANITY_CHECKS
+
+#define H5C2__AUX_DLL_PRE_REMOVE_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) \
+if ( ( (hd_ptr) == NULL ) || \
+ ( (tail_ptr) == NULL ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (len) <= 0 ) || \
+ ( (Size) < (entry_ptr)->size ) || \
+ ( ( (Size) == (entry_ptr)->size ) && ( ! ( (len) == 1 ) ) ) || \
+ ( ( (entry_ptr)->aux_prev == NULL ) && ( (hd_ptr) != (entry_ptr) ) ) || \
+ ( ( (entry_ptr)->aux_next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \
+ ( ( (len) == 1 ) && \
+ ( ! ( ( (hd_ptr) == (entry_ptr) ) && ( (tail_ptr) == (entry_ptr) ) && \
+ ( (entry_ptr)->aux_next == NULL ) && \
+ ( (entry_ptr)->aux_prev == NULL ) && \
+ ( (Size) == (entry_ptr)->size ) \
+ ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "aux DLL pre remove SC failed") \
+}
+
+#define H5C2__AUX_DLL_SC(head_ptr, tail_ptr, len, Size, fv) \
+if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
+ ( (head_ptr) != (tail_ptr) ) \
+ ) || \
+ ( (len) < 0 ) || \
+ ( (Size) < 0 ) || \
+ ( ( (len) == 1 ) && \
+ ( ( (head_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \
+ ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \
+ ) \
+ ) || \
+ ( ( (len) >= 1 ) && \
+ ( ( (head_ptr) == NULL ) || ( (head_ptr)->aux_prev != NULL ) || \
+ ( (tail_ptr) == NULL ) || ( (tail_ptr)->aux_next != NULL ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "AUX DLL sanity check failed") \
+}
+
+#define H5C2__AUX_DLL_PRE_INSERT_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) \
+if ( ( (entry_ptr) == NULL ) || \
+ ( (entry_ptr)->aux_next != NULL ) || \
+ ( (entry_ptr)->aux_prev != NULL ) || \
+ ( ( ( (hd_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
+ ( (hd_ptr) != (tail_ptr) ) \
+ ) || \
+ ( (len) < 0 ) || \
+ ( ( (len) == 1 ) && \
+ ( ( (hd_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \
+ ( (hd_ptr) == NULL ) || ( (hd_ptr)->size != (Size) ) \
+ ) \
+ ) || \
+ ( ( (len) >= 1 ) && \
+ ( ( (hd_ptr) == NULL ) || ( (hd_ptr)->aux_prev != NULL ) || \
+ ( (tail_ptr) == NULL ) || ( (tail_ptr)->aux_next != NULL ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "AUX DLL pre insert SC failed") \
+}
+
+#else /* H5C2_DO_SANITY_CHECKS */
+
+#define H5C2__AUX_DLL_PRE_REMOVE_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv)
+#define H5C2__AUX_DLL_SC(head_ptr, tail_ptr, len, Size, fv)
+#define H5C2__AUX_DLL_PRE_INSERT_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv)
+
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+
+#define H5C2__AUX_DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val)\
+ H5C2__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ if ( (head_ptr) == NULL ) \
+ { \
+ (head_ptr) = (entry_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ else \
+ { \
+ (tail_ptr)->aux_next = (entry_ptr); \
+ (entry_ptr)->aux_prev = (tail_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ (len)++; \
+ (Size) += entry_ptr->size;
+
+#define H5C2__AUX_DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+ H5C2__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fv) \
+ if ( (head_ptr) == NULL ) \
+ { \
+ (head_ptr) = (entry_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ else \
+ { \
+ (head_ptr)->aux_prev = (entry_ptr); \
+ (entry_ptr)->aux_next = (head_ptr); \
+ (head_ptr) = (entry_ptr); \
+ } \
+ (len)++; \
+ (Size) += entry_ptr->size;
+
+#define H5C2__AUX_DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+ H5C2__AUX_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fv) \
+ { \
+ if ( (head_ptr) == (entry_ptr) ) \
+ { \
+ (head_ptr) = (entry_ptr)->aux_next; \
+ if ( (head_ptr) != NULL ) \
+ { \
+ (head_ptr)->aux_prev = NULL; \
+ } \
+ } \
+ else \
+ { \
+ (entry_ptr)->aux_prev->aux_next = (entry_ptr)->aux_next; \
+ } \
+ if ( (tail_ptr) == (entry_ptr) ) \
+ { \
+ (tail_ptr) = (entry_ptr)->aux_prev; \
+ if ( (tail_ptr) != NULL ) \
+ { \
+ (tail_ptr)->aux_next = NULL; \
+ } \
+ } \
+ else \
+ { \
+ (entry_ptr)->aux_next->aux_prev = (entry_ptr)->aux_prev; \
+ } \
+ entry_ptr->aux_next = NULL; \
+ entry_ptr->aux_prev = NULL; \
+ (len)--; \
+ (Size) -= entry_ptr->size; \
+ }
+
+
+/***********************************************************************
+ *
+ * Stats collection macros
+ *
+ * The following macros must handle stats collection when this collection
+ * is enabled, and evaluate to the empty string when it is not.
+ *
+ * The sole exception to this rule is
+ * H5C2__UPDATE_CACHE_HIT_RATE_STATS(), which is always active as
+ * the cache hit rate stats are always collected and available.
+ *
+ * Changes:
+ *
+ * JRM -- 3/21/06
+ * Added / updated macros for pinned entry related stats.
+ *
+ * JRM -- 8/9/06
+ * More pinned entry stats related updates.
+ *
+ * JRM -- 3/31/07
+ * Updated H5C2__UPDATE_STATS_FOR_PROTECT() to keep stats on
+ * read and write protects.
+ *
+ ***********************************************************************/
+
+#define H5C2__UPDATE_CACHE_HIT_RATE_STATS(cache_ptr, hit) \
+ (cache_ptr->cache_accesses)++; \
+ if ( hit ) { \
+ (cache_ptr->cache_hits)++; \
+ } \
+
+#if H5C2_COLLECT_CACHE_STATS
+
+#define H5C2__UPDATE_STATS_FOR_DIRTY_PIN(cache_ptr, entry_ptr) \
+ (((cache_ptr)->dirty_pins)[(entry_ptr)->type->id])++;
+
+#define H5C2__UPDATE_STATS_FOR_UNPROTECT(cache_ptr) \
+ if ( (cache_ptr)->slist_len > (cache_ptr)->max_slist_len ) \
+ (cache_ptr)->max_slist_len = (cache_ptr)->slist_len; \
+ if ( (cache_ptr)->slist_size > (cache_ptr)->max_slist_size ) \
+ (cache_ptr)->max_slist_size = (cache_ptr)->slist_size; \
+ if ( (cache_ptr)->pel_len > (cache_ptr)->max_pel_len ) \
+ (cache_ptr)->max_pel_len = (cache_ptr)->pel_len; \
+ if ( (cache_ptr)->pel_size > (cache_ptr)->max_pel_size ) \
+ (cache_ptr)->max_pel_size = (cache_ptr)->pel_size;
+
+#define H5C2__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) \
+ if ( cache_ptr->flush_in_progress ) { \
+ ((cache_ptr)->cache_flush_renames[(entry_ptr)->type->id])++; \
+ } \
+ if ( entry_ptr->flush_in_progress ) { \
+ ((cache_ptr)->entry_flush_renames[(entry_ptr)->type->id])++; \
+ } \
+ (((cache_ptr)->renames)[(entry_ptr)->type->id])++;
+
+#define H5C2__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_size)\
+ if ( cache_ptr->flush_in_progress ) { \
+ ((cache_ptr)->cache_flush_size_changes[(entry_ptr)->type->id])++; \
+ } \
+ if ( entry_ptr->flush_in_progress ) { \
+ ((cache_ptr)->entry_flush_size_changes[(entry_ptr)->type->id])++; \
+ } \
+ if ( (entry_ptr)->size < (new_size) ) { \
+ ((cache_ptr)->size_increases[(entry_ptr)->type->id])++; \
+ if ( (cache_ptr)->index_size > (cache_ptr)->max_index_size ) \
+ (cache_ptr)->max_index_size = (cache_ptr)->index_size; \
+ if ( (cache_ptr)->slist_size > (cache_ptr)->max_slist_size ) \
+ (cache_ptr)->max_slist_size = (cache_ptr)->slist_size; \
+ if ( (cache_ptr)->pl_size > (cache_ptr)->max_pl_size ) \
+ (cache_ptr)->max_pl_size = (cache_ptr)->pl_size; \
+ } else if ( (entry_ptr)->size > (new_size) ) { \
+ ((cache_ptr)->size_decreases[(entry_ptr)->type->id])++; \
+ }
+
+#define H5C2__UPDATE_STATS_FOR_HT_INSERTION(cache_ptr) \
+ (cache_ptr)->total_ht_insertions++;
+
+#define H5C2__UPDATE_STATS_FOR_HT_DELETION(cache_ptr) \
+ (cache_ptr)->total_ht_deletions++;
+
+#define H5C2__UPDATE_STATS_FOR_HT_SEARCH(cache_ptr, success, depth) \
+ if ( success ) { \
+ (cache_ptr)->successful_ht_searches++; \
+ (cache_ptr)->total_successful_ht_search_depth += depth; \
+ } else { \
+ (cache_ptr)->failed_ht_searches++; \
+ (cache_ptr)->total_failed_ht_search_depth += depth; \
+ }
+
+#define H5C2__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr) \
+ ((cache_ptr)->unpins)[(entry_ptr)->type->id]++;
+
+#if H5C2_COLLECT_CACHE_ENTRY_STATS
+
+#define H5C2__RESET_CACHE_ENTRY_STATS(entry_ptr) \
+ (entry_ptr)->accesses = 0; \
+ (entry_ptr)->clears = 0; \
+ (entry_ptr)->flushes = 0; \
+ (entry_ptr)->pins = 0;
+
+#define H5C2__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) \
+ (((cache_ptr)->clears)[(entry_ptr)->type->id])++; \
+ if ( (entry_ptr)->is_pinned ) { \
+ (((cache_ptr)->pinned_clears)[(entry_ptr)->type->id])++; \
+ } \
+ ((entry_ptr)->clears)++;
+
+#define H5C2__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) \
+ (((cache_ptr)->flushes)[(entry_ptr)->type->id])++; \
+ if ( (entry_ptr)->is_pinned ) { \
+ (((cache_ptr)->pinned_flushes)[(entry_ptr)->type->id])++; \
+ } \
+ ((entry_ptr)->flushes)++;
+
+#define H5C2__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) \
+ (((cache_ptr)->evictions)[(entry_ptr)->type->id])++; \
+ if ( (entry_ptr)->accesses > \
+ ((cache_ptr)->max_accesses)[(entry_ptr)->type->id] ) { \
+ ((cache_ptr)->max_accesses)[(entry_ptr)->type->id] \
+ = (entry_ptr)->accesses; \
+ } \
+ if ( (entry_ptr)->accesses < \
+ ((cache_ptr)->min_accesses)[(entry_ptr)->type->id] ) { \
+ ((cache_ptr)->min_accesses)[(entry_ptr)->type->id] \
+ = (entry_ptr)->accesses; \
+ } \
+ if ( (entry_ptr)->clears > \
+ ((cache_ptr)->max_clears)[(entry_ptr)->type->id] ) { \
+ ((cache_ptr)->max_clears)[(entry_ptr)->type->id] \
+ = (entry_ptr)->clears; \
+ } \
+ if ( (entry_ptr)->flushes > \
+ ((cache_ptr)->max_flushes)[(entry_ptr)->type->id] ) { \
+ ((cache_ptr)->max_flushes)[(entry_ptr)->type->id] \
+ = (entry_ptr)->flushes; \
+ } \
+ if ( (entry_ptr)->size > \
+ ((cache_ptr)->max_size)[(entry_ptr)->type->id] ) { \
+ ((cache_ptr)->max_size)[(entry_ptr)->type->id] \
+ = (entry_ptr)->size; \
+ } \
+ if ( (entry_ptr)->pins > \
+ ((cache_ptr)->max_pins)[(entry_ptr)->type->id] ) { \
+ ((cache_ptr)->max_pins)[(entry_ptr)->type->id] \
+ = (entry_ptr)->pins; \
+ }
+
+#define H5C2__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) \
+ (((cache_ptr)->insertions)[(entry_ptr)->type->id])++; \
+ if ( (entry_ptr)->is_pinned ) { \
+ (((cache_ptr)->pinned_insertions)[(entry_ptr)->type->id])++; \
+ ((cache_ptr)->pins)[(entry_ptr)->type->id]++; \
+ (entry_ptr)->pins++; \
+ if ( (cache_ptr)->pel_len > (cache_ptr)->max_pel_len ) \
+ (cache_ptr)->max_pel_len = (cache_ptr)->pel_len; \
+ if ( (cache_ptr)->pel_size > (cache_ptr)->max_pel_size ) \
+ (cache_ptr)->max_pel_size = (cache_ptr)->pel_size; \
+ } \
+ if ( (cache_ptr)->index_len > (cache_ptr)->max_index_len ) \
+ (cache_ptr)->max_index_len = (cache_ptr)->index_len; \
+ if ( (cache_ptr)->index_size > (cache_ptr)->max_index_size ) \
+ (cache_ptr)->max_index_size = (cache_ptr)->index_size; \
+ if ( (cache_ptr)->slist_len > (cache_ptr)->max_slist_len ) \
+ (cache_ptr)->max_slist_len = (cache_ptr)->slist_len; \
+ if ( (cache_ptr)->slist_size > (cache_ptr)->max_slist_size ) \
+ (cache_ptr)->max_slist_size = (cache_ptr)->slist_size; \
+ if ( (entry_ptr)->size > \
+ ((cache_ptr)->max_size)[(entry_ptr)->type->id] ) { \
+ ((cache_ptr)->max_size)[(entry_ptr)->type->id] \
+ = (entry_ptr)->size; \
+ }
+
+#define H5C2__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) \
+ if ( hit ) \
+ ((cache_ptr)->hits)[(entry_ptr)->type->id]++; \
+ else \
+ ((cache_ptr)->misses)[(entry_ptr)->type->id]++; \
+ if ( ! ((entry_ptr)->is_read_only) ) { \
+ ((cache_ptr)->write_protects)[(entry_ptr)->type->id]++; \
+ } else { \
+ ((cache_ptr)->read_protects)[(entry_ptr)->type->id]++; \
+ if ( ((entry_ptr)->ro_ref_count) > \
+ ((cache_ptr)->max_read_protects)[(entry_ptr)->type->id] ) { \
+ ((cache_ptr)->max_read_protects)[(entry_ptr)->type->id] = \
+ ((entry_ptr)->ro_ref_count); \
+ } \
+ } \
+ if ( (cache_ptr)->index_len > (cache_ptr)->max_index_len ) \
+ (cache_ptr)->max_index_len = (cache_ptr)->index_len; \
+ if ( (cache_ptr)->index_size > (cache_ptr)->max_index_size ) \
+ (cache_ptr)->max_index_size = (cache_ptr)->index_size; \
+ if ( (cache_ptr)->pl_len > (cache_ptr)->max_pl_len ) \
+ (cache_ptr)->max_pl_len = (cache_ptr)->pl_len; \
+ if ( (cache_ptr)->pl_size > (cache_ptr)->max_pl_size ) \
+ (cache_ptr)->max_pl_size = (cache_ptr)->pl_size; \
+ if ( (entry_ptr)->size > \
+ ((cache_ptr)->max_size)[(entry_ptr)->type->id] ) { \
+ ((cache_ptr)->max_size)[(entry_ptr)->type->id] \
+ = (entry_ptr)->size; \
+ } \
+ ((entry_ptr)->accesses)++;
+
+#define H5C2__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr) \
+ ((cache_ptr)->pins)[(entry_ptr)->type->id]++; \
+ (entry_ptr)->pins++; \
+ if ( (cache_ptr)->pel_len > (cache_ptr)->max_pel_len ) \
+ (cache_ptr)->max_pel_len = (cache_ptr)->pel_len; \
+ if ( (cache_ptr)->pel_size > (cache_ptr)->max_pel_size ) \
+ (cache_ptr)->max_pel_size = (cache_ptr)->pel_size;
+
+#else /* H5C2_COLLECT_CACHE_ENTRY_STATS */
+
+#define H5C2__RESET_CACHE_ENTRY_STATS(entry_ptr)
+
+#define H5C2__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) \
+ if ( (entry_ptr)->is_pinned ) { \
+ (((cache_ptr)->pinned_clears)[(entry_ptr)->type->id])++; \
+ } \
+ (((cache_ptr)->clears)[(entry_ptr)->type->id])++;
+
+#define H5C2__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) \
+ (((cache_ptr)->flushes)[(entry_ptr)->type->id])++; \
+ if ( (entry_ptr)->is_pinned ) { \
+ (((cache_ptr)->pinned_flushes)[(entry_ptr)->type->id])++; \
+ }
+
+#define H5C2__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) \
+ (((cache_ptr)->evictions)[(entry_ptr)->type->id])++;
+
+#define H5C2__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) \
+ (((cache_ptr)->insertions)[(entry_ptr)->type->id])++; \
+ if ( (entry_ptr)->is_pinned ) { \
+ (((cache_ptr)->pinned_insertions)[(entry_ptr)->type->id])++; \
+ ((cache_ptr)->pins)[(entry_ptr)->type->id]++; \
+ if ( (cache_ptr)->pel_len > (cache_ptr)->max_pel_len ) \
+ (cache_ptr)->max_pel_len = (cache_ptr)->pel_len; \
+ if ( (cache_ptr)->pel_size > (cache_ptr)->max_pel_size ) \
+ (cache_ptr)->max_pel_size = (cache_ptr)->pel_size; \
+ } \
+ if ( (cache_ptr)->index_len > (cache_ptr)->max_index_len ) \
+ (cache_ptr)->max_index_len = (cache_ptr)->index_len; \
+ if ( (cache_ptr)->index_size > (cache_ptr)->max_index_size ) \
+ (cache_ptr)->max_index_size = (cache_ptr)->index_size; \
+ if ( (cache_ptr)->slist_len > (cache_ptr)->max_slist_len ) \
+ (cache_ptr)->max_slist_len = (cache_ptr)->slist_len; \
+ if ( (cache_ptr)->slist_size > (cache_ptr)->max_slist_size ) \
+ (cache_ptr)->max_slist_size = (cache_ptr)->slist_size;
+
+#define H5C2__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) \
+ if ( hit ) \
+ ((cache_ptr)->hits)[(entry_ptr)->type->id]++; \
+ else \
+ ((cache_ptr)->misses)[(entry_ptr)->type->id]++; \
+ if ( ! ((entry_ptr)->is_read_only) ) { \
+ ((cache_ptr)->write_protects)[(entry_ptr)->type->id]++; \
+ } else { \
+ ((cache_ptr)->read_protects)[(entry_ptr)->type->id]++; \
+ if ( ((entry_ptr)->ro_ref_count) > \
+ ((cache_ptr)->max_read_protects)[(entry_ptr)->type->id] ) { \
+ ((cache_ptr)->max_read_protects)[(entry_ptr)->type->id] = \
+ ((entry_ptr)->ro_ref_count); \
+ } \
+ } \
+ if ( (cache_ptr)->index_len > (cache_ptr)->max_index_len ) \
+ (cache_ptr)->max_index_len = (cache_ptr)->index_len; \
+ if ( (cache_ptr)->index_size > (cache_ptr)->max_index_size ) \
+ (cache_ptr)->max_index_size = (cache_ptr)->index_size; \
+ if ( (cache_ptr)->pl_len > (cache_ptr)->max_pl_len ) \
+ (cache_ptr)->max_pl_len = (cache_ptr)->pl_len; \
+ if ( (cache_ptr)->pl_size > (cache_ptr)->max_pl_size ) \
+ (cache_ptr)->max_pl_size = (cache_ptr)->pl_size;
+
+#define H5C2__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr) \
+ ((cache_ptr)->pins)[(entry_ptr)->type->id]++; \
+ if ( (cache_ptr)->pel_len > (cache_ptr)->max_pel_len ) \
+ (cache_ptr)->max_pel_len = (cache_ptr)->pel_len; \
+ if ( (cache_ptr)->pel_size > (cache_ptr)->max_pel_size ) \
+ (cache_ptr)->max_pel_size = (cache_ptr)->pel_size;
+
+#endif /* H5C2_COLLECT_CACHE_ENTRY_STATS */
+
+#else /* H5C2_COLLECT_CACHE_STATS */
+
+#define H5C2__RESET_CACHE_ENTRY_STATS(entry_ptr)
+#define H5C2__UPDATE_STATS_FOR_DIRTY_PIN(cache_ptr, entry_ptr)
+#define H5C2__UPDATE_STATS_FOR_UNPROTECT(cache_ptr)
+#define H5C2__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr)
+#define H5C2__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_size)
+#define H5C2__UPDATE_STATS_FOR_HT_INSERTION(cache_ptr)
+#define H5C2__UPDATE_STATS_FOR_HT_DELETION(cache_ptr)
+#define H5C2__UPDATE_STATS_FOR_HT_SEARCH(cache_ptr, success, depth)
+#define H5C2__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr)
+#define H5C2__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr)
+#define H5C2__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr)
+#define H5C2__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr)
+#define H5C2__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit)
+#define H5C2__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr)
+#define H5C2__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr)
+
+#endif /* H5C2_COLLECT_CACHE_STATS */
+
+
+/***********************************************************************
+ *
+ * Hash table access and manipulation macros:
+ *
+ * The following macros handle searches, insertions, and deletion in
+ * the hash table.
+ *
+ * When modifying these macros, remember to modify the similar macros
+ * in tst/cache.c
+ *
+ ***********************************************************************/
+
+/* H5C2__HASH_TABLE_LEN is defined in H5C2pkg.h. It mut be a power of two. */
+
+#define H5C2__HASH_MASK ((size_t)(H5C2__HASH_TABLE_LEN - 1) << 3)
+
+#define H5C2__HASH_FCN(x) (int)(((x) & H5C2__HASH_MASK) >> 3)
+
+#if H5C2_DO_SANITY_CHECKS
+
+#define H5C2__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val) \
+if ( ( (cache_ptr) == NULL ) || \
+ ( (cache_ptr)->magic != H5C2__H5C2_T_MAGIC ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( ! H5F_addr_defined((entry_ptr)->addr) ) || \
+ ( (entry_ptr)->ht_next != NULL ) || \
+ ( (entry_ptr)->ht_prev != NULL ) || \
+ ( (entry_ptr)->size <= 0 ) || \
+ ( (k = H5C2__HASH_FCN((entry_ptr)->addr)) < 0 ) || \
+ ( k >= H5C2__HASH_TABLE_LEN ) ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \
+ "Pre HT insert SC failed") \
+}
+
+#define H5C2__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) \
+if ( ( (cache_ptr) == NULL ) || \
+ ( (cache_ptr)->magic != H5C2__H5C2_T_MAGIC ) || \
+ ( (cache_ptr)->index_len < 1 ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (cache_ptr)->index_size < (entry_ptr)->size ) || \
+ ( ! H5F_addr_defined((entry_ptr)->addr) ) || \
+ ( (entry_ptr)->size <= 0 ) || \
+ ( H5C2__HASH_FCN((entry_ptr)->addr) < 0 ) || \
+ ( H5C2__HASH_FCN((entry_ptr)->addr) >= H5C2__HASH_TABLE_LEN ) || \
+ ( ((cache_ptr)->index)[(H5C2__HASH_FCN((entry_ptr)->addr))] \
+ == NULL ) || \
+ ( ( ((cache_ptr)->index)[(H5C2__HASH_FCN((entry_ptr)->addr))] \
+ != (entry_ptr) ) && \
+ ( (entry_ptr)->ht_prev == NULL ) ) || \
+ ( ( ((cache_ptr)->index)[(H5C2__HASH_FCN((entry_ptr)->addr))] == \
+ (entry_ptr) ) && \
+ ( (entry_ptr)->ht_prev != NULL ) ) ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Pre HT remove SC failed") \
+}
+
+#define H5C2__PRE_HT_SEARCH_SC(cache_ptr, Addr, fail_val) \
+if ( ( (cache_ptr) == NULL ) || \
+ ( (cache_ptr)->magic != H5C2__H5C2_T_MAGIC ) || \
+ ( ! H5F_addr_defined(Addr) ) || \
+ ( H5C2__HASH_FCN(Addr) < 0 ) || \
+ ( H5C2__HASH_FCN(Addr) >= H5C2__HASH_TABLE_LEN ) ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, "Pre HT search SC failed") \
+}
+
+#define H5C2__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k, fail_val) \
+if ( ( (cache_ptr) == NULL ) || \
+ ( (cache_ptr)->magic != H5C2__H5C2_T_MAGIC ) || \
+ ( (cache_ptr)->index_len < 1 ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (cache_ptr)->index_size < (entry_ptr)->size ) || \
+ ( H5F_addr_ne((entry_ptr)->addr, (Addr)) ) || \
+ ( (entry_ptr)->size <= 0 ) || \
+ ( ((cache_ptr)->index)[k] == NULL ) || \
+ ( ( ((cache_ptr)->index)[k] != (entry_ptr) ) && \
+ ( (entry_ptr)->ht_prev == NULL ) ) || \
+ ( ( ((cache_ptr)->index)[k] == (entry_ptr) ) && \
+ ( (entry_ptr)->ht_prev != NULL ) ) || \
+ ( ( (entry_ptr)->ht_prev != NULL ) && \
+ ( (entry_ptr)->ht_prev->ht_next != (entry_ptr) ) ) || \
+ ( ( (entry_ptr)->ht_next != NULL ) && \
+ ( (entry_ptr)->ht_next->ht_prev != (entry_ptr) ) ) ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \
+ "Post successful HT search SC failed") \
+}
+
+#define H5C2__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val) \
+if ( ( (cache_ptr) == NULL ) || \
+ ( ((cache_ptr)->index)[k] != (entry_ptr) ) || \
+ ( (entry_ptr)->ht_prev != NULL ) ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \
+ "Post HT shift to front SC failed") \
+}
+
+#define H5C2__PRE_HT_ENTRY_SIZE_CHANGE_SC(cache_ptr, old_size, new_size) \
+if ( ( (cache_ptr) == NULL ) || \
+ ( (cache_ptr)->index_len <= 0 ) || \
+ ( (cache_ptr)->index_size <= 0 ) || \
+ ( (new_size) <= 0 ) || \
+ ( (old_size) > (cache_ptr)->index_size ) || \
+ ( (new_size) <= 0 ) || \
+ ( ( (cache_ptr)->index_len == 1 ) && \
+ ( (cache_ptr)->index_size != (old_size) ) ) ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Pre HT entry size change SC failed") \
+}
+
+#define H5C2__POST_HT_ENTRY_SIZE_CHANGE_SC(cache_ptr, old_size, new_size) \
+if ( ( (cache_ptr) == NULL ) || \
+ ( (cache_ptr)->index_len <= 0 ) || \
+ ( (cache_ptr)->index_size <= 0 ) || \
+ ( (new_size) > (cache_ptr)->index_size ) || \
+ ( ( (cache_ptr)->index_len == 1 ) && \
+ ( (cache_ptr)->index_size != (new_size) ) ) ) { \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Post HT entry size change SC failed") \
+}
+
+#else /* H5C2_DO_SANITY_CHECKS */
+
+#define H5C2__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val)
+#define H5C2__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr)
+#define H5C2__PRE_HT_SEARCH_SC(cache_ptr, Addr, fail_val)
+#define H5C2__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k, fail_val)
+#define H5C2__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val)
+#define H5C2__PRE_HT_ENTRY_SIZE_CHANGE_SC(cache_ptr, old_size, new_size)
+#define H5C2__POST_HT_ENTRY_SIZE_CHANGE_SC(cache_ptr, old_size, new_size)
+
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+
+#define H5C2__INSERT_IN_INDEX(cache_ptr, entry_ptr, fail_val) \
+{ \
+ int k; \
+ H5C2__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val) \
+ k = H5C2__HASH_FCN((entry_ptr)->addr); \
+ if ( ((cache_ptr)->index)[k] == NULL ) \
+ { \
+ ((cache_ptr)->index)[k] = (entry_ptr); \
+ } \
+ else \
+ { \
+ (entry_ptr)->ht_next = ((cache_ptr)->index)[k]; \
+ (entry_ptr)->ht_next->ht_prev = (entry_ptr); \
+ ((cache_ptr)->index)[k] = (entry_ptr); \
+ } \
+ (cache_ptr)->index_len++; \
+ (cache_ptr)->index_size += (entry_ptr)->size; \
+ H5C2__UPDATE_STATS_FOR_HT_INSERTION(cache_ptr) \
+}
+
+#define H5C2__DELETE_FROM_INDEX(cache_ptr, entry_ptr) \
+{ \
+ int k; \
+ H5C2__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) \
+ k = H5C2__HASH_FCN((entry_ptr)->addr); \
+ if ( (entry_ptr)->ht_next ) \
+ { \
+ (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \
+ } \
+ if ( (entry_ptr)->ht_prev ) \
+ { \
+ (entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \
+ } \
+ if ( ((cache_ptr)->index)[k] == (entry_ptr) ) \
+ { \
+ ((cache_ptr)->index)[k] = (entry_ptr)->ht_next; \
+ } \
+ (entry_ptr)->ht_next = NULL; \
+ (entry_ptr)->ht_prev = NULL; \
+ (cache_ptr)->index_len--; \
+ (cache_ptr)->index_size -= (entry_ptr)->size; \
+ H5C2__UPDATE_STATS_FOR_HT_DELETION(cache_ptr) \
+}
+
+#define H5C2__SEARCH_INDEX(cache_ptr, Addr, entry_ptr, fail_val) \
+{ \
+ int k; \
+ int depth = 0; \
+ H5C2__PRE_HT_SEARCH_SC(cache_ptr, Addr, fail_val) \
+ k = H5C2__HASH_FCN(Addr); \
+ entry_ptr = ((cache_ptr)->index)[k]; \
+ while ( ( entry_ptr ) && ( H5F_addr_ne(Addr, (entry_ptr)->addr) ) ) \
+ { \
+ (entry_ptr) = (entry_ptr)->ht_next; \
+ (depth)++; \
+ } \
+ if ( entry_ptr ) \
+ { \
+ H5C2__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k, fail_val) \
+ if ( entry_ptr != ((cache_ptr)->index)[k] ) \
+ { \
+ if ( (entry_ptr)->ht_next ) \
+ { \
+ (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \
+ } \
+ HDassert( (entry_ptr)->ht_prev != NULL ); \
+ (entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \
+ ((cache_ptr)->index)[k]->ht_prev = (entry_ptr); \
+ (entry_ptr)->ht_next = ((cache_ptr)->index)[k]; \
+ (entry_ptr)->ht_prev = NULL; \
+ ((cache_ptr)->index)[k] = (entry_ptr); \
+ H5C2__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val) \
+ } \
+ } \
+ H5C2__UPDATE_STATS_FOR_HT_SEARCH(cache_ptr, (entry_ptr != NULL), depth) \
+}
+
+#define H5C2__SEARCH_INDEX_NO_STATS(cache_ptr, Addr, entry_ptr, fail_val) \
+{ \
+ int k; \
+ int depth = 0; \
+ H5C2__PRE_HT_SEARCH_SC(cache_ptr, Addr, fail_val) \
+ k = H5C2__HASH_FCN(Addr); \
+ entry_ptr = ((cache_ptr)->index)[k]; \
+ while ( ( entry_ptr ) && ( H5F_addr_ne(Addr, (entry_ptr)->addr) ) ) \
+ { \
+ (entry_ptr) = (entry_ptr)->ht_next; \
+ (depth)++; \
+ } \
+ if ( entry_ptr ) \
+ { \
+ H5C2__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k, fail_val) \
+ if ( entry_ptr != ((cache_ptr)->index)[k] ) \
+ { \
+ if ( (entry_ptr)->ht_next ) \
+ { \
+ (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \
+ } \
+ HDassert( (entry_ptr)->ht_prev != NULL ); \
+ (entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \
+ ((cache_ptr)->index)[k]->ht_prev = (entry_ptr); \
+ (entry_ptr)->ht_next = ((cache_ptr)->index)[k]; \
+ (entry_ptr)->ht_prev = NULL; \
+ ((cache_ptr)->index)[k] = (entry_ptr); \
+ H5C2__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val) \
+ } \
+ } \
+}
+
+#define H5C2__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, old_size, new_size) \
+{ \
+ H5C2__PRE_HT_ENTRY_SIZE_CHANGE_SC(cache_ptr, old_size, new_size) \
+ (cache_ptr)->index_size -= old_size; \
+ (cache_ptr)->index_size += new_size; \
+ H5C2__POST_HT_ENTRY_SIZE_CHANGE_SC(cache_ptr, old_size, new_size) \
+}
+
+
+/**************************************************************************
+ *
+ * Skip list insertion and deletion macros:
+ *
+ * These used to be functions, but I converted them to macros to avoid some
+ * function call overhead.
+ *
+ **************************************************************************/
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5C2__INSERT_ENTRY_IN_SLIST
+ *
+ * Purpose: Insert the specified instance of H5C2_cache_entry_t into
+ * the skip list in the specified instance of H5C2_t. Update
+ * the associated length and size fields.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 5/10/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/21/04
+ * Updated function to set the in_tree flag when inserting
+ * an entry into the tree. Also modified the function to
+ * update the tree size and len fields instead of the similar
+ * index fields.
+ *
+ * All of this is part of the modifications to support the
+ * hash table.
+ *
+ * JRM -- 7/27/04
+ * Converted the function H5C2_insert_entry_in_tree() into
+ * the macro H5C2__INSERT_ENTRY_IN_TREE in the hopes of
+ * wringing a little more speed out of the cache.
+ *
+ * Note that we don't bother to check if the entry is already
+ * in the tree -- if it is, H5SL_insert() will fail.
+ *
+ * QAK -- 11/27/04
+ * Switched over to using skip list routines.
+ *
+ * JRM -- 6/27/06
+ * Added fail_val parameter.
+ *
+ * JRM -- 8/25/06
+ * Added the H5C2_DO_SANITY_CHECKS version of the macro.
+ *
+ * This version maintains the slist_len_increase and
+ * slist_size_increase fields that are used in sanity
+ * checks in the flush routines.
+ *
+ * All this is needed as the fractal heap needs to be
+ * able to dirty, resize and/or rename entries during the
+ * flush.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_DO_SANITY_CHECKS
+
+#define H5C2__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ HDassert( H5F_addr_defined((entry_ptr)->addr) ); \
+ HDassert( !((entry_ptr)->in_slist) ); \
+ \
+ if ( H5SL_insert((cache_ptr)->slist_ptr, entry_ptr, &(entry_ptr)->addr) \
+ < 0 ) \
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, (fail_val), \
+ "Can't insert entry in skip list") \
+ \
+ (entry_ptr)->in_slist = TRUE; \
+ (cache_ptr)->slist_len++; \
+ (cache_ptr)->slist_size += (entry_ptr)->size; \
+ (cache_ptr)->slist_len_increase++; \
+ (cache_ptr)->slist_size_increase += (entry_ptr)->size; \
+ \
+ HDassert( (cache_ptr)->slist_len > 0 ); \
+ HDassert( (cache_ptr)->slist_size > 0 ); \
+ \
+} /* H5C2__INSERT_ENTRY_IN_SLIST */
+
+#else /* H5C2_DO_SANITY_CHECKS */
+
+#define H5C2__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ HDassert( H5F_addr_defined((entry_ptr)->addr) ); \
+ HDassert( !((entry_ptr)->in_slist) ); \
+ \
+ if ( H5SL_insert((cache_ptr)->slist_ptr, entry_ptr, &(entry_ptr)->addr) \
+ < 0 ) \
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, (fail_val), \
+ "Can't insert entry in skip list") \
+ \
+ (entry_ptr)->in_slist = TRUE; \
+ (cache_ptr)->slist_len++; \
+ (cache_ptr)->slist_size += (entry_ptr)->size; \
+ \
+ HDassert( (cache_ptr)->slist_len > 0 ); \
+ HDassert( (cache_ptr)->slist_size > 0 ); \
+ \
+} /* H5C2__INSERT_ENTRY_IN_SLIST */
+
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2__REMOVE_ENTRY_FROM_SLIST
+ *
+ * Purpose: Remove the specified instance of H5C2_cache_entry_t from the
+ * index skip list in the specified instance of H5C2_t. Update
+ * the associated length and size fields.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 5/10/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/21/04
+ * Updated function for the addition of the hash table.
+ *
+ * JRM - 7/27/04
+ * Converted from the function H5C2_remove_entry_from_tree()
+ * to the macro H5C2__REMOVE_ENTRY_FROM_TREE in the hopes of
+ * wringing a little more performance out of the cache.
+ *
+ * QAK -- 11/27/04
+ * Switched over to using skip list routines.
+ *
+ * JRM -- 3/28/07
+ * Updated sanity checks for the new is_read_only and
+ * ro_ref_count fields in H5C2_cache_entry_t.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5C2__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ HDassert( (entry_ptr)->in_slist ); \
+ HDassert( (cache_ptr)->slist_ptr ); \
+ \
+ if ( H5SL_remove((cache_ptr)->slist_ptr, &(entry_ptr)->addr) \
+ != (entry_ptr) ) \
+ \
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, \
+ "Can't delete entry from skip list.") \
+ \
+ HDassert( (cache_ptr)->slist_len > 0 ); \
+ (cache_ptr)->slist_len--; \
+ HDassert( (cache_ptr)->slist_size >= (entry_ptr)->size ); \
+ (cache_ptr)->slist_size -= (entry_ptr)->size; \
+ (entry_ptr)->in_slist = FALSE; \
+} /* H5C2__REMOVE_ENTRY_FROM_SLIST */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2__UPDATE_SLIST_FOR_SIZE_CHANGE
+ *
+ * Purpose: Update cache_ptr->slist_size for a change in the size of
+ * and entry in the slist.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 9/07/05
+ *
+ * Modifications:
+ *
+ * JRM -- 8/27/06
+ * Added the H5C2_DO_SANITY_CHECKS version of the macro.
+ *
+ * This version maintains the slist_size_increase field
+ * that are used in sanity checks in the flush routines.
+ *
+ * All this is needed as the fractal heap needs to be
+ * able to dirty, resize and/or rename entries during the
+ * flush.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_DO_SANITY_CHECKS
+
+#define H5C2__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, old_size, new_size) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (old_size) > 0 ); \
+ HDassert( (new_size) > 0 ); \
+ HDassert( (old_size) <= (cache_ptr)->slist_size ); \
+ HDassert( (cache_ptr)->slist_len > 0 ); \
+ HDassert( ((cache_ptr)->slist_len > 1) || \
+ ( (cache_ptr)->slist_size == (old_size) ) ); \
+ \
+ (cache_ptr)->slist_size -= (old_size); \
+ (cache_ptr)->slist_size += (new_size); \
+ \
+ (cache_ptr)->slist_size_increase -= (int64_t)(old_size); \
+ (cache_ptr)->slist_size_increase += (int64_t)(new_size); \
+ \
+ HDassert( (new_size) <= (cache_ptr)->slist_size ); \
+ HDassert( ( (cache_ptr)->slist_len > 1 ) || \
+ ( (cache_ptr)->slist_size == (new_size) ) ); \
+} /* H5C2__REMOVE_ENTRY_FROM_SLIST */
+
+#else /* H5C2_DO_SANITY_CHECKS */
+
+#define H5C2__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, old_size, new_size) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (old_size) > 0 ); \
+ HDassert( (new_size) > 0 ); \
+ HDassert( (old_size) <= (cache_ptr)->slist_size ); \
+ HDassert( (cache_ptr)->slist_len > 0 ); \
+ HDassert( ((cache_ptr)->slist_len > 1) || \
+ ( (cache_ptr)->slist_size == (old_size) ) ); \
+ \
+ (cache_ptr)->slist_size -= (old_size); \
+ (cache_ptr)->slist_size += (new_size); \
+ \
+ HDassert( (new_size) <= (cache_ptr)->slist_size ); \
+ HDassert( ( (cache_ptr)->slist_len > 1 ) || \
+ ( (cache_ptr)->slist_size == (new_size) ) ); \
+} /* H5C2__REMOVE_ENTRY_FROM_SLIST */
+
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+
+/**************************************************************************
+ *
+ * Replacement policy update macros:
+ *
+ * These used to be functions, but I converted them to macros to avoid some
+ * function call overhead.
+ *
+ **************************************************************************/
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5C2__FAKE_RP_FOR_MOST_RECENT_ACCESS
+ *
+ * Purpose: For efficiency, we sometimes change the order of flushes --
+ * but doing so can confuse the replacement policy. This
+ * macro exists to allow us to specify an entry as the
+ * most recently touched so we can repair any such
+ * confusion.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the macro
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 10/13/05
+ *
+ * Modifications:
+ *
+ * JRM -- 3/20/06
+ * Modified macro to ignore pinned entries. Pinned entries
+ * do not appear in the data structures maintained by the
+ * replacement policy code, and thus this macro has nothing
+ * to do if called for such an entry.
+ *
+ * JRM -- 3/28/07
+ * Added sanity checks using the new is_read_only and
+ * ro_ref_count fields of struct H5C2_cache_entry_t.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+#define H5C2__FAKE_RP_FOR_MOST_RECENT_ACCESS(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ if ( ! ((entry_ptr)->is_pinned) ) { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list, and re-insert it at the head.\
+ */ \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* Use the dirty flag to infer whether the entry is on the clean or \
+ * dirty LRU list, and remove it. Then insert it at the head of \
+ * the same LRU list. \
+ * \
+ * At least initially, all entries should be clean. That may \
+ * change, so we may as well deal with both cases now. \
+ */ \
+ \
+ if ( (entry_ptr)->is_dirty ) { \
+ H5C2__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ } else { \
+ H5C2__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ } \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+} /* H5C2__FAKE_RP_FOR_MOST_RECENT_ACCESS */
+
+#else /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+#define H5C2__FAKE_RP_FOR_MOST_RECENT_ACCESS(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ if ( ! ((entry_ptr)->is_pinned) ) { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list, and re-insert it at the head \
+ */ \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+} /* H5C2__FAKE_RP_FOR_MOST_RECENT_ACCESS */
+
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5C2__UPDATE_RP_FOR_EVICTION
+ *
+ * Purpose: Update the replacement policy data structures for an
+ * eviction of the specified cache entry.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 5/10/04
+ *
+ * Modifications:
+ *
+ * JRM - 7/27/04
+ * Converted the function H5C2_update_rp_for_eviction() to the
+ * macro H5C2__UPDATE_RP_FOR_EVICTION in an effort to squeeze
+ * a bit more performance out of the cache.
+ *
+ * At least for the first cut, I am leaving the comments and
+ * white space in the macro. If they cause dificulties with
+ * the pre-processor, I'll have to remove them.
+ *
+ * JRM - 7/28/04
+ * Split macro into two version, one supporting the clean and
+ * dirty LRU lists, and the other not. Yet another attempt
+ * at optimization.
+ *
+ * JRM - 3/20/06
+ * Pinned entries can't be evicted, so this entry should never
+ * be called on a pinned entry. Added assert to verify this.
+ *
+ * JRM -- 3/28/07
+ * Added sanity checks for the new is_read_only and
+ * ro_ref_count fields of struct H5C2_cache_entry_t.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+#define H5C2__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( !((entry_ptr)->is_pinned) ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list. */ \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* If the entry is clean when it is evicted, it should be on the \
+ * clean LRU list, if it was dirty, it should be on the dirty LRU list. \
+ * Remove it from the appropriate list according to the value of the \
+ * dirty flag. \
+ */ \
+ \
+ if ( (entry_ptr)->is_dirty ) { \
+ \
+ H5C2__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ } else { \
+ H5C2__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ } \
+ \
+} /* H5C2__UPDATE_RP_FOR_EVICTION */
+
+#else /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+#define H5C2__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( !((entry_ptr)->is_pinned) ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list. */ \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+} /* H5C2__UPDATE_RP_FOR_EVICTION */
+
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5C2__UPDATE_RP_FOR_FLUSH
+ *
+ * Purpose: Update the replacement policy data structures for a flush
+ * of the specified cache entry.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 5/6/04
+ *
+ * Modifications:
+ *
+ * JRM - 7/27/04
+ * Converted the function H5C2_update_rp_for_flush() to the
+ * macro H5C2__UPDATE_RP_FOR_FLUSH in an effort to squeeze
+ * a bit more performance out of the cache.
+ *
+ * At least for the first cut, I am leaving the comments and
+ * white space in the macro. If they cause dificulties with
+ * pre-processor, I'll have to remove them.
+ *
+ * JRM - 7/28/04
+ * Split macro into two versions, one supporting the clean and
+ * dirty LRU lists, and the other not. Yet another attempt
+ * at optimization.
+ *
+ * JRM - 3/20/06
+ * While pinned entries can be flushed, they don't reside in
+ * the replacement policy data structures when unprotected.
+ * Thus I modified this macro to do nothing if the entry is
+ * pinned.
+ *
+ * JRM - 3/28/07
+ * Added sanity checks based on the new is_read_only and
+ * ro_ref_count fields of struct H5C2_cache_entry_t.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+#define H5C2__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ if ( ! ((entry_ptr)->is_pinned) ) { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list, and re-insert it at the \
+ * head. \
+ */ \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* since the entry is being flushed or cleared, one would think \
+ * that it must be dirty -- but that need not be the case. Use the \
+ * dirty flag to infer whether the entry is on the clean or dirty \
+ * LRU list, and remove it. Then insert it at the head of the \
+ * clean LRU list. \
+ * \
+ * The function presumes that a dirty entry will be either cleared \
+ * or flushed shortly, so it is OK if we put a dirty entry on the \
+ * clean LRU list. \
+ */ \
+ \
+ if ( (entry_ptr)->is_dirty ) { \
+ H5C2__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ } else { \
+ H5C2__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ } \
+ \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+} /* H5C2__UPDATE_RP_FOR_FLUSH */
+
+#else /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+#define H5C2__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ if ( ! ((entry_ptr)->is_pinned) ) { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list, and re-insert it at the \
+ * head. \
+ */ \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+} /* H5C2__UPDATE_RP_FOR_FLUSH */
+
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5C2__UPDATE_RP_FOR_INSERTION
+ *
+ * Purpose: Update the replacement policy data structures for an
+ * insertion of the specified cache entry.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 5/17/04
+ *
+ * Modifications:
+ *
+ * JRM - 7/27/04
+ * Converted the function H5C2_update_rp_for_insertion() to the
+ * macro H5C2__UPDATE_RP_FOR_INSERTION in an effort to squeeze
+ * a bit more performance out of the cache.
+ *
+ * At least for the first cut, I am leaving the comments and
+ * white space in the macro. If they cause dificulties with
+ * pre-processor, I'll have to remove them.
+ *
+ * JRM - 7/28/04
+ * Split macro into two version, one supporting the clean and
+ * dirty LRU lists, and the other not. Yet another attempt
+ * at optimization.
+ *
+ * JRM - 3/10/06
+ * This macro should never be called on a pinned entry.
+ * Inserted an assert to verify this.
+ *
+ * JRM - 8/9/06
+ * Not any more. We must now allow insertion of pinned
+ * entries. Updated macro to support this.
+ *
+ * JRM - 3/28/07
+ * Added sanity checks using the new is_read_only and
+ * ro_ref_count fields of struct H5C2_cache_entry_t.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+#define H5C2__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ if ( (entry_ptr)->is_pinned ) { \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->pel_head_ptr, \
+ (cache_ptr)->pel_tail_ptr, \
+ (cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, (fail_val)) \
+ \
+ } else { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* insert the entry at the head of the LRU list. */ \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* insert the entry at the head of the clean or dirty LRU list as \
+ * appropriate. \
+ */ \
+ \
+ if ( entry_ptr->is_dirty ) { \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ } else { \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ } \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+}
+
+#else /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+#define H5C2__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ if ( (entry_ptr)->is_pinned ) { \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->pel_head_ptr, \
+ (cache_ptr)->pel_tail_ptr, \
+ (cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, (fail_val)) \
+ \
+ } else { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* insert the entry at the head of the LRU list. */ \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+}
+
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5C2__UPDATE_RP_FOR_PROTECT
+ *
+ * Purpose: Update the replacement policy data structures for a
+ * protect of the specified cache entry.
+ *
+ * To do this, unlink the specified entry from any data
+ * structures used by the replacement policy, and add the
+ * entry to the protected list.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 5/17/04
+ *
+ * Modifications:
+ *
+ * JRM - 7/27/04
+ * Converted the function H5C2_update_rp_for_protect() to the
+ * macro H5C2__UPDATE_RP_FOR_PROTECT in an effort to squeeze
+ * a bit more performance out of the cache.
+ *
+ * At least for the first cut, I am leaving the comments and
+ * white space in the macro. If they cause dificulties with
+ * pre-processor, I'll have to remove them.
+ *
+ * JRM - 7/28/04
+ * Split macro into two version, one supporting the clean and
+ * dirty LRU lists, and the other not. Yet another attempt
+ * at optimization.
+ *
+ * JRM - 3/17/06
+ * Modified macro to attempt to remove pinned entriese from
+ * the pinned entry list instead of from the data structures
+ * maintained by the replacement policy.
+ *
+ * JRM - 3/28/07
+ * Added sanity checks based on the new is_read_only and
+ * ro_ref_count fields of struct H5C2_cache_entry_t.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+#define H5C2__UPDATE_RP_FOR_PROTECT(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ if ( (entry_ptr)->is_pinned ) { \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->pel_head_ptr, \
+ (cache_ptr)->pel_tail_ptr, \
+ (cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, (fail_val)) \
+ \
+ } else { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list. */ \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* Similarly, remove the entry from the clean or dirty LRU list \
+ * as appropriate. \
+ */ \
+ \
+ if ( (entry_ptr)->is_dirty ) { \
+ \
+ H5C2__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ \
+ } else { \
+ \
+ H5C2__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ } \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+ \
+ /* Regardless of the replacement policy, or whether the entry is \
+ * pinned, now add the entry to the protected list. \
+ */ \
+ \
+ H5C2__DLL_APPEND((entry_ptr), (cache_ptr)->pl_head_ptr, \
+ (cache_ptr)->pl_tail_ptr, \
+ (cache_ptr)->pl_len, \
+ (cache_ptr)->pl_size, (fail_val)) \
+} /* H5C2__UPDATE_RP_FOR_PROTECT */
+
+#else /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+#define H5C2__UPDATE_RP_FOR_PROTECT(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ if ( (entry_ptr)->is_pinned ) { \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->pel_head_ptr, \
+ (cache_ptr)->pel_tail_ptr, \
+ (cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, (fail_val)) \
+ \
+ } else { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list. */ \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+ \
+ /* Regardless of the replacement policy, or whether the entry is \
+ * pinned, now add the entry to the protected list. \
+ */ \
+ \
+ H5C2__DLL_APPEND((entry_ptr), (cache_ptr)->pl_head_ptr, \
+ (cache_ptr)->pl_tail_ptr, \
+ (cache_ptr)->pl_len, \
+ (cache_ptr)->pl_size, (fail_val)) \
+} /* H5C2__UPDATE_RP_FOR_PROTECT */
+
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5C2__UPDATE_RP_FOR_RENAME
+ *
+ * Purpose: Update the replacement policy data structures for a
+ * rename of the specified cache entry.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 5/17/04
+ *
+ * Modifications:
+ *
+ * JRM - 7/27/04
+ * Converted the function H5C2_update_rp_for_rename() to the
+ * macro H5C2__UPDATE_RP_FOR_RENAME in an effort to squeeze
+ * a bit more performance out of the cache.
+ *
+ * At least for the first cut, I am leaving the comments and
+ * white space in the macro. If they cause dificulties with
+ * pre-processor, I'll have to remove them.
+ *
+ * JRM - 7/28/04
+ * Split macro into two version, one supporting the clean and
+ * dirty LRU lists, and the other not. Yet another attempt
+ * at optimization.
+ *
+ * JRM - 6/23/05
+ * Added the was_dirty parameter. It is possible that
+ * the entry was clean when it was renamed -- if so it
+ * it is in the clean LRU regardless of the current
+ * value of the is_dirty field.
+ *
+ * At present, all renamed entries are forced to be
+ * dirty. This macro is a bit more general that that,
+ * to allow it to function correctly should that policy
+ * be relaxed in the future.
+ *
+ * JRM - 3/17/06
+ * Modified macro to do nothing if the entry is pinned.
+ * In this case, the entry is on the pinned entry list, not
+ * in the replacement policy data structures, so there is
+ * nothing to be done.
+ *
+ * JRM - 3/28/07
+ * Added sanity checks using the new is_read_only and
+ * ro_ref_count fields of struct H5C2_cache_entry_t.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+#define H5C2__UPDATE_RP_FOR_RENAME(cache_ptr, entry_ptr, was_dirty, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ if ( ! ((entry_ptr)->is_pinned) ) { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list, and re-insert it at the head. \
+ */ \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* remove the entry from either the clean or dirty LUR list as \
+ * indicated by the was_dirty parameter \
+ */ \
+ if ( was_dirty ) { \
+ \
+ H5C2__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ \
+ } else { \
+ \
+ H5C2__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ } \
+ \
+ /* insert the entry at the head of either the clean or dirty LRU \
+ * list as appropriate. \
+ */ \
+ \
+ if ( (entry_ptr)->is_dirty ) { \
+ \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ \
+ } else { \
+ \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ } \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+} /* H5C2__UPDATE_RP_FOR_RENAME */
+
+#else /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+#define H5C2__UPDATE_RP_FOR_RENAME(cache_ptr, entry_ptr, was_dirty, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ if ( ! ((entry_ptr)->is_pinned) ) { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list, and re-insert it at the head. \
+ */ \
+ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+} /* H5C2__UPDATE_RP_FOR_RENAME */
+
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5C2__UPDATE_RP_FOR_SIZE_CHANGE
+ *
+ * Purpose: Update the replacement policy data structures for a
+ * size change of the specified cache entry.
+ *
+ * To do this, determine if the entry is pinned. If it is,
+ * update the size of the pinned entry list.
+ *
+ * If it isn't pinned, the entry must handled by the
+ * replacement policy. Update the appropriate replacement
+ * policy data structures.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 8/23/06
+ *
+ * Modifications:
+ *
+ * JRM -- 3/28/07
+ * Added sanity checks based on the new is_read_only and
+ * ro_ref_count fields of struct H5C2_cache_entry_t.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+#define H5C2__UPDATE_RP_FOR_SIZE_CHANGE(cache_ptr, entry_ptr, new_size) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ HDassert( new_size > 0 ); \
+ \
+ if ( (entry_ptr)->is_pinned ) { \
+ \
+ H5C2__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, \
+ (entry_ptr)->size, \
+ (new_size)); \
+ \
+ } else { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* Update the size of the LRU list */ \
+ \
+ H5C2__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, \
+ (entry_ptr)->size, \
+ (new_size)); \
+ \
+ /* Similarly, update the size of the clean or dirty LRU list as \
+ * appropriate. At present, the entry must be clean, but that \
+ * could change. \
+ */ \
+ \
+ if ( (entry_ptr)->is_dirty ) { \
+ \
+ H5C2__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, \
+ (entry_ptr)->size, \
+ (new_size)); \
+ \
+ } else { \
+ \
+ H5C2__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, \
+ (entry_ptr)->size, \
+ (new_size)); \
+ } \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+ \
+} /* H5C2__UPDATE_RP_FOR_SIZE_CHANGE */
+
+#else /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+#define H5C2__UPDATE_RP_FOR_SIZE_CHANGE(cache_ptr, entry_ptr, new_size) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ HDassert( new_size > 0 ); \
+ \
+ if ( (entry_ptr)->is_pinned ) { \
+ \
+ H5C2__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, \
+ (entry_ptr)->size, \
+ (new_size)); \
+ \
+ } else { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* Update the size of the LRU list */ \
+ \
+ H5C2__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, \
+ (entry_ptr)->size, \
+ (new_size)); \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+ \
+} /* H5C2__UPDATE_RP_FOR_SIZE_CHANGE */
+
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5C2__UPDATE_RP_FOR_UNPIN
+ *
+ * Purpose: Update the replacement policy data structures for an
+ * unpin of the specified cache entry.
+ *
+ * To do this, unlink the specified entry from the protected
+ * entry list, and re-insert it in the data structures used
+ * by the current replacement policy.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the macro
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 3/22/06
+ *
+ * Modifications:
+ *
+ * JRM -- 3/28/07
+ * Added sanity checks based on the new is_read_only and
+ * ro_ref_count fields of struct H5C2_cache_entry_t.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+#define H5C2__UPDATE_RP_FOR_UNPIN(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->is_pinned); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ /* Regardless of the replacement policy, remove the entry from the \
+ * pinned entry list. \
+ */ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->pel_head_ptr, \
+ (cache_ptr)->pel_tail_ptr, (cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, (fail_val)) \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* insert the entry at the head of the LRU list. */ \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* Similarly, insert the entry at the head of either the clean or \
+ * dirty LRU list as appropriate. \
+ */ \
+ \
+ if ( (entry_ptr)->is_dirty ) { \
+ \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ \
+ } else { \
+ \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ } \
+ \
+ /* End modified LRU specific code. */ \
+ \
+} /* H5C2__UPDATE_RP_FOR_UNPIN */
+
+#else /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+#define H5C2__UPDATE_RP_FOR_UNPIN(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( !((entry_ptr)->is_protected) ); \
+ HDassert( !((entry_ptr)->is_read_only) ); \
+ HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
+ HDassert( (entry_ptr)->is_pinned); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ /* Regardless of the replacement policy, remove the entry from the \
+ * pinned entry list. \
+ */ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->pel_head_ptr, \
+ (cache_ptr)->pel_tail_ptr, (cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, (fail_val)) \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* insert the entry at the head of the LRU list. */ \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ \
+} /* H5C2__UPDATE_RP_FOR_UNPIN */
+
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5C2__UPDATE_RP_FOR_UNPROTECT
+ *
+ * Purpose: Update the replacement policy data structures for an
+ * unprotect of the specified cache entry.
+ *
+ * To do this, unlink the specified entry from the protected
+ * list, and re-insert it in the data structures used by the
+ * current replacement policy.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 5/19/04
+ *
+ * Modifications:
+ *
+ * JRM - 7/27/04
+ * Converted the function H5C2_update_rp_for_unprotect() to
+ * the macro H5C2__UPDATE_RP_FOR_UNPROTECT in an effort to
+ * squeeze a bit more performance out of the cache.
+ *
+ * At least for the first cut, I am leaving the comments and
+ * white space in the macro. If they cause dificulties with
+ * pre-processor, I'll have to remove them.
+ *
+ * JRM - 7/28/04
+ * Split macro into two version, one supporting the clean and
+ * dirty LRU lists, and the other not. Yet another attempt
+ * at optimization.
+ *
+ * JRM - 3/17/06
+ * Modified macro to put pinned entries on the pinned entry
+ * list instead of inserting them in the data structures
+ * maintained by the replacement policy.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+#define H5C2__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->is_protected); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ /* Regardless of the replacement policy, remove the entry from the \
+ * protected list. \
+ */ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->pl_head_ptr, \
+ (cache_ptr)->pl_tail_ptr, (cache_ptr)->pl_len, \
+ (cache_ptr)->pl_size, (fail_val)) \
+ \
+ if ( (entry_ptr)->is_pinned ) { \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->pel_head_ptr, \
+ (cache_ptr)->pel_tail_ptr, \
+ (cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, (fail_val)) \
+ \
+ } else { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* insert the entry at the head of the LRU list. */ \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* Similarly, insert the entry at the head of either the clean or \
+ * dirty LRU list as appropriate. \
+ */ \
+ \
+ if ( (entry_ptr)->is_dirty ) { \
+ \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ \
+ } else { \
+ \
+ H5C2__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ } \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+ \
+} /* H5C2__UPDATE_RP_FOR_UNPROTECT */
+
+#else /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+#define H5C2__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (cache_ptr) ); \
+ HDassert( (cache_ptr)->magic == H5C2__H5C2_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->is_protected); \
+ HDassert( (entry_ptr)->size > 0 ); \
+ \
+ /* Regardless of the replacement policy, remove the entry from the \
+ * protected list. \
+ */ \
+ H5C2__DLL_REMOVE((entry_ptr), (cache_ptr)->pl_head_ptr, \
+ (cache_ptr)->pl_tail_ptr, (cache_ptr)->pl_len, \
+ (cache_ptr)->pl_size, (fail_val)) \
+ \
+ if ( (entry_ptr)->is_pinned ) { \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->pel_head_ptr, \
+ (cache_ptr)->pel_tail_ptr, \
+ (cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, (fail_val)) \
+ \
+ } else { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* insert the entry at the head of the LRU list. */ \
+ \
+ H5C2__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+} /* H5C2__UPDATE_RP_FOR_UNPROTECT */
+
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+
+/*
+ * Private file-scope variables.
+ */
+
+/* Declare a free list to manage the H5C2_t struct */
+H5FL_DEFINE_STATIC(H5C2_t);
+
+
+/*
+ * Private file-scope function declarations:
+ */
+
+static herr_t H5C2__auto_adjust_cache_size(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ hbool_t write_permitted);
+
+static herr_t H5C2__autoadjust__ageout(H5C2_t * cache_ptr,
+ double hit_rate,
+ enum H5C2_resize_status * status_ptr,
+ size_t * new_max_cache_size_ptr,
+ hid_t dxpl_id,
+ hbool_t write_permitted);
+
+static herr_t H5C2__autoadjust__ageout__cycle_epoch_marker(H5C2_t * cache_ptr);
+
+static herr_t H5C2__autoadjust__ageout__evict_aged_out_entries(hid_t dxpl_id,
+ H5C2_t * cache_ptr,
+ hbool_t write_permitted);
+
+static herr_t H5C2__autoadjust__ageout__insert_new_marker(H5C2_t * cache_ptr);
+
+static herr_t H5C2__autoadjust__ageout__remove_all_markers(H5C2_t * cache_ptr);
+
+static herr_t H5C2__autoadjust__ageout__remove_excess_markers(H5C2_t * cache_ptr);
+
+static herr_t H5C2_flush_single_entry(H5F_t * f,
+ hid_t dxpl_id,
+ H5C2_t * cache_ptr,
+ const H5C2_class_t * type_ptr,
+ haddr_t addr,
+ unsigned flags,
+ hbool_t del_entry_from_slist_on_destroy);
+
+static herr_t H5C2_flush_invalidate_cache(hid_t dxpl_id,
+ H5C2_t * cache_ptr,
+ unsigned flags);
+
+static void * H5C2_load_entry(H5F_t * f,
+ hid_t dxpl_id,
+ const H5C2_class_t * type,
+ haddr_t addr,
+ size_t len,
+ hbool_t chk_len,
+ const void * udata_ptr);
+
+static herr_t H5C2_make_space_in_cache(hid_t dxpl_id,
+ H5C2_t * cache_ptr,
+ size_t space_needed,
+ hbool_t write_permitted);
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+static herr_t H5C2_validate_lru_list(H5C2_t * cache_ptr);
+static herr_t H5C2_verify_not_in_index(H5C2_t * cache_ptr,
+ H5C2_cache_entry_t * entry_ptr);
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+
+/****************************************************************************
+ *
+ * #defines and declarations for epoch marker cache entries.
+ *
+ * As a strategy for automatic cache size reduction, the cache may insert
+ * marker entries in the LRU list at the end of each epoch. These markers
+ * are then used to identify entries that have not been accessed for n
+ * epochs so that they can be evicted from the cache.
+ *
+ ****************************************************************************/
+
+/* Note that H5C2__MAX_EPOCH_MARKERS is defined in H5C2pkg.h, not here because
+ * it is needed to dimension arrays in H5C2_t.
+ */
+
+#define H5C2__EPOCH_MARKER_TYPE H5C2__MAX_NUM_TYPE_IDS
+
+static void * H5C2_epoch_marker_deserialize(haddr_t addr,
+ size_t len,
+ const void * image_ptr,
+ const void * udata_ptr,
+ hbool_t * dirty_ptr);
+static herr_t H5C2_epoch_marker_image_len(void * thing,
+ size_t *image_len_ptr);
+static herr_t H5C2_epoch_marker_serialize(haddr_t addr,
+ size_t len,
+ void * image_ptr,
+ void * thing,
+ unsigned * flags_ptr,
+ haddr_t * new_addr_ptr,
+ size_t * new_len_ptr,
+ void ** new_image_ptr_ptr);
+static herr_t H5C2_epoch_marker_free_icr(haddr_t addr,
+ size_t len,
+ void * thing);
+static herr_t H5C2_epoch_marker_clear_dirty_bits(haddr_t addr,
+ size_t len,
+ void * thing);
+
+const H5C2_class_t epoch_marker_class_2 =
+{
+ /* id = */ H5C2__EPOCH_MARKER_TYPE,
+ /* name = */ "epoch marker",
+ /* mem_type = */ H5FD_MEM_DEFAULT, /* value doesn't matter */
+ /* deserialize = */ &H5C2_epoch_marker_deserialize,
+ /* image_len = */ &H5C2_epoch_marker_image_len,
+ /* serialize = */ &H5C2_epoch_marker_serialize,
+ /* free_icr = */ &H5C2_epoch_marker_free_icr,
+ /* clear_dirty_bits = */ &H5C2_epoch_marker_clear_dirty_bits,
+};
+
+
+/***************************************************************************
+ * Class functions for H5C2__EPOCH_MAKER_TYPE:
+ *
+ * None of these functions should ever be called, so there is no point in
+ * documenting them separately.
+ * JRM - 11/16/04
+ *
+ ***************************************************************************/
+
+static void *
+H5C2_epoch_marker_deserialize(haddr_t UNUSED addr,
+ size_t UNUSED len,
+ const void UNUSED * image_ptr,
+ const void UNUSED * udata_ptr,
+ hbool_t UNUSED * dirty_ptr)
+{
+ void * ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_epoch_marker_serialize, NULL)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "called unreachable fcn.")
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+static herr_t
+H5C2_epoch_marker_image_len(void UNUSED *thing,
+ size_t UNUSED *image_len_ptr)
+{
+ herr_t ret_value = FAIL; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_epoch_marker_image_len, FAIL)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "called unreachable fcn.")
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+static herr_t
+H5C2_epoch_marker_serialize(haddr_t UNUSED addr,
+ size_t UNUSED len,
+ void UNUSED * image_ptr,
+ void UNUSED * thing,
+ unsigned UNUSED * flags_ptr,
+ haddr_t UNUSED * new_addr_ptr,
+ size_t UNUSED * new_len_ptr,
+ void UNUSED ** new_image_ptr_ptr)
+{
+ herr_t ret_value = FAIL; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_epoch_marker_serialize, FAIL)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "called unreachable fcn.")
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+static herr_t
+H5C2_epoch_marker_free_icr(haddr_t UNUSED addr,
+ size_t UNUSED len,
+ void UNUSED * thing)
+{
+ herr_t ret_value = FAIL; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_epoch_marker_free_icr, FAIL)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "called unreachable fcn.")
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+static herr_t
+H5C2_epoch_marker_clear_dirty_bits(haddr_t UNUSED addr,
+ size_t UNUSED len,
+ void UNUSED * thing)
+{
+ herr_t ret_value = FAIL; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_epoch_marker_clear_dirty_bits, FAIL)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "called unreachable fcn.")
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_create
+ *
+ * Purpose: Allocate, initialize, and return the address of a new
+ * instance of H5C2_t.
+ *
+ * In general, the max_cache_size parameter must be positive,
+ * and the min_clean_size parameter must lie in the closed
+ * interval [0, max_cache_size].
+ *
+ * The check_write_permitted parameter must either be NULL,
+ * or point to a function of type H5C2_write_permitted_func_t.
+ * If it is NULL, the cache will use the write_permitted
+ * flag to determine whether writes are permitted.
+ *
+ * Return: Success: Pointer to the new instance.
+ *
+ * Failure: NULL
+ *
+ * Programmer: John Mainzer
+ * 6/2/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/20/04
+ * Updated for the addition of the hash table.
+ *
+ * JRM -- 10/5/04
+ * Added call to H5C2_reset_cache_hit_rate_stats(). Also
+ * added initialization for cache_is_full flag and for
+ * resize_ctl.
+ *
+ * JRM -- 11/12/04
+ * Added initialization for the new size_decreased field.
+ *
+ * JRM -- 11/17/04
+ * Added/updated initialization for the automatic cache
+ * size control data structures.
+ *
+ * JRM -- 6/24/05
+ * Added support for the new write_permitted field of
+ * the H5C2_t structure.
+ *
+ * JRM -- 7/5/05
+ * Added the new log_flush parameter and supporting code.
+ *
+ * JRM -- 9/21/05
+ * Added the new aux_ptr parameter and supporting code.
+ *
+ * JRM -- 1/20/06
+ * Added initialization of the new prefix field in H5C2_t.
+ *
+ * JRM -- 3/16/06
+ * Added initialization for the pinned entry related fields.
+ *
+ * JRM -- 5/31/06
+ * Added initialization for the trace_file_ptr field.
+ *
+ * JRM -- 8/19/06
+ * Added initialization for the flush_in_progress field.
+ *
+ * JRM -- 8/25/06
+ * Added initialization for the slist_len_increase and
+ * slist_size_increase fields. These fields are used
+ * for sanity checking in the flush process, and are not
+ * compiled in unless H5C2_DO_SANITY_CHECKS is TRUE.
+ *
+ * JRM -- 3/28/07
+ * Added initialization for the new is_read_only and
+ * ro_ref_count fields.
+ *
+ * JRM -- 7/10/07
+ * Added the f parameter, along with initialization of
+ * the field of the same in in H5C2_t. Also removed the
+ * type name table, as type names are now included in
+ * instances of H5C2_class_t.
+ *
+ * JRM -- 3/28/07
+ * Added initialization for the new is_read_only and
+ * ro_ref_count fields.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+H5C2_t *
+H5C2_create(const H5F_t * f,
+ size_t max_cache_size,
+ size_t min_clean_size,
+ int max_type_id,
+ const char * (* type_name_table_ptr),
+ H5C2_write_permitted_func_t check_write_permitted,
+ hbool_t write_permitted,
+ H5C2_log_flush_func_t log_flush,
+ void * aux_ptr)
+{
+ int i;
+ H5C2_t * cache_ptr = NULL;
+ H5C2_t * ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_create, NULL)
+
+ HDassert( f );
+
+ HDassert( max_cache_size >= H5C2__MIN_MAX_CACHE_SIZE );
+ HDassert( max_cache_size <= H5C2__MAX_MAX_CACHE_SIZE );
+ HDassert( min_clean_size <= max_cache_size );
+
+ HDassert( max_type_id >= 0 );
+ HDassert( max_type_id < H5C2__MAX_NUM_TYPE_IDS );
+
+ HDassert( ( write_permitted == TRUE ) || ( write_permitted == FALSE ) );
+
+ for ( i = 0; i <= max_type_id; i++ ) {
+
+ HDassert( (type_name_table_ptr)[i] );
+ HDassert( HDstrlen(( type_name_table_ptr)[i]) > 0 );
+ }
+
+ if ( NULL == (cache_ptr = H5FL_CALLOC(H5C2_t)) ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, \
+ "memory allocation failed")
+ }
+
+ if ( (cache_ptr->slist_ptr = H5SL_create(H5SL_TYPE_HADDR,0.5,(size_t)16))
+ == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, NULL, "can't create skip list.")
+ }
+
+ /* If we get this far, we should succeed. Go ahead and initialize all
+ * the fields.
+ */
+
+ cache_ptr->magic = H5C2__H5C2_T_MAGIC;
+
+ cache_ptr->f = (H5F_t *)f;
+
+ cache_ptr->flush_in_progress = FALSE;
+
+ cache_ptr->trace_file_ptr = NULL;
+
+ cache_ptr->aux_ptr = aux_ptr;
+
+ cache_ptr->max_type_id = max_type_id;
+
+ cache_ptr->type_name_table_ptr = type_name_table_ptr;
+
+ cache_ptr->max_cache_size = max_cache_size;
+ cache_ptr->min_clean_size = min_clean_size;
+
+ cache_ptr->check_write_permitted = check_write_permitted;
+ cache_ptr->write_permitted = write_permitted;
+
+ cache_ptr->log_flush = log_flush;
+
+ cache_ptr->evictions_enabled = TRUE;
+
+ cache_ptr->index_len = 0;
+ cache_ptr->index_size = (size_t)0;
+
+ cache_ptr->slist_len = 0;
+ cache_ptr->slist_size = (size_t)0;
+
+#if H5C2_DO_SANITY_CHECKS
+ cache_ptr->slist_len_increase = 0;
+ cache_ptr->slist_size_increase = 0;
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ for ( i = 0; i < H5C2__HASH_TABLE_LEN; i++ )
+ {
+ (cache_ptr->index)[i] = NULL;
+ }
+
+ cache_ptr->pl_len = 0;
+ cache_ptr->pl_size = (size_t)0;
+ cache_ptr->pl_head_ptr = NULL;
+ cache_ptr->pl_tail_ptr = NULL;
+
+ cache_ptr->pel_len = 0;
+ cache_ptr->pel_size = (size_t)0;
+ cache_ptr->pel_head_ptr = NULL;
+ cache_ptr->pel_tail_ptr = NULL;
+
+ cache_ptr->LRU_list_len = 0;
+ cache_ptr->LRU_list_size = (size_t)0;
+ cache_ptr->LRU_head_ptr = NULL;
+ cache_ptr->LRU_tail_ptr = NULL;
+
+ cache_ptr->cLRU_list_len = 0;
+ cache_ptr->cLRU_list_size = (size_t)0;
+ cache_ptr->cLRU_head_ptr = NULL;
+ cache_ptr->cLRU_tail_ptr = NULL;
+
+ cache_ptr->dLRU_list_len = 0;
+ cache_ptr->dLRU_list_size = (size_t)0;
+ cache_ptr->dLRU_head_ptr = NULL;
+ cache_ptr->dLRU_tail_ptr = NULL;
+
+ cache_ptr->size_increase_possible = FALSE;
+ cache_ptr->size_decrease_possible = FALSE;
+ cache_ptr->resize_enabled = FALSE;
+ cache_ptr->cache_full = FALSE;
+ cache_ptr->size_decreased = FALSE;
+
+ (cache_ptr->resize_ctl).version = H5C2__CURR_AUTO_SIZE_CTL_VER;
+ (cache_ptr->resize_ctl).rpt_fcn = NULL;
+ (cache_ptr->resize_ctl).set_initial_size = FALSE;
+ (cache_ptr->resize_ctl).initial_size = H5C2__DEF_AR_INIT_SIZE;
+ (cache_ptr->resize_ctl).min_clean_fraction = H5C2__DEF_AR_MIN_CLEAN_FRAC;
+ (cache_ptr->resize_ctl).max_size = H5C2__DEF_AR_MAX_SIZE;
+ (cache_ptr->resize_ctl).min_size = H5C2__DEF_AR_MIN_SIZE;
+ (cache_ptr->resize_ctl).epoch_length = H5C2__DEF_AR_EPOCH_LENGTH;
+
+ (cache_ptr->resize_ctl).incr_mode = H5C2_incr__off;
+ (cache_ptr->resize_ctl).lower_hr_threshold = H5C2__DEF_AR_LOWER_THRESHHOLD;
+ (cache_ptr->resize_ctl).increment = H5C2__DEF_AR_INCREMENT;
+ (cache_ptr->resize_ctl).apply_max_increment = TRUE;
+ (cache_ptr->resize_ctl).max_increment = H5C2__DEF_AR_MAX_INCREMENT;
+
+ (cache_ptr->resize_ctl).decr_mode = H5C2_decr__off;
+ (cache_ptr->resize_ctl).upper_hr_threshold = H5C2__DEF_AR_UPPER_THRESHHOLD;
+ (cache_ptr->resize_ctl).decrement = H5C2__DEF_AR_DECREMENT;
+ (cache_ptr->resize_ctl).apply_max_decrement = TRUE;
+ (cache_ptr->resize_ctl).max_decrement = H5C2__DEF_AR_MAX_DECREMENT;
+ (cache_ptr->resize_ctl).epochs_before_eviction = H5C2__DEF_AR_EPCHS_B4_EVICT;
+ (cache_ptr->resize_ctl).apply_empty_reserve = TRUE;
+ (cache_ptr->resize_ctl).empty_reserve = H5C2__DEF_AR_EMPTY_RESERVE;
+
+ cache_ptr->epoch_markers_active = 0;
+
+ /* no need to initialize the ring buffer itself */
+ cache_ptr->epoch_marker_ringbuf_first = 1;
+ cache_ptr->epoch_marker_ringbuf_last = 0;
+ cache_ptr->epoch_marker_ringbuf_size = 0;
+
+ for ( i = 0; i < H5C2__MAX_EPOCH_MARKERS; i++ )
+ {
+ (cache_ptr->epoch_marker_active)[i] = FALSE;
+
+ ((cache_ptr->epoch_markers)[i]).magic =
+ H5C2__H5C2_CACHE_ENTRY_T_MAGIC;
+ ((cache_ptr->epoch_markers)[i]).addr = (haddr_t)i;
+ ((cache_ptr->epoch_markers)[i]).size = (size_t)0;
+ ((cache_ptr->epoch_markers)[i]).type = &epoch_marker_class_2;
+ ((cache_ptr->epoch_markers)[i]).is_dirty = FALSE;
+ ((cache_ptr->epoch_markers)[i]).dirtied = FALSE;
+ ((cache_ptr->epoch_markers)[i]).is_protected = FALSE;
+ ((cache_ptr->epoch_markers)[i]).is_read_only = FALSE;
+ ((cache_ptr->epoch_markers)[i]).ro_ref_count = 0;
+ ((cache_ptr->epoch_markers)[i]).is_pinned = FALSE;
+ ((cache_ptr->epoch_markers)[i]).in_slist = FALSE;
+ ((cache_ptr->epoch_markers)[i]).ht_next = NULL;
+ ((cache_ptr->epoch_markers)[i]).ht_prev = NULL;
+ ((cache_ptr->epoch_markers)[i]).next = NULL;
+ ((cache_ptr->epoch_markers)[i]).prev = NULL;
+ ((cache_ptr->epoch_markers)[i]).aux_next = NULL;
+ ((cache_ptr->epoch_markers)[i]).aux_prev = NULL;
+#if H5C2_COLLECT_CACHE_ENTRY_STATS
+ ((cache_ptr->epoch_markers)[i]).accesses = 0;
+ ((cache_ptr->epoch_markers)[i]).clears = 0;
+ ((cache_ptr->epoch_markers)[i]).flushes = 0;
+ ((cache_ptr->epoch_markers)[i]).pins = 0;
+#endif /* H5C2_COLLECT_CACHE_ENTRY_STATS */
+ }
+
+ if ( H5C2_reset_cache_hit_rate_stats(cache_ptr) != SUCCEED ) {
+
+ /* this should be impossible... */
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, \
+ "H5C2_reset_cache_hit_rate_stats failed.")
+ }
+
+ H5C2_stats__reset(cache_ptr);
+
+ cache_ptr->skip_file_checks = FALSE;
+ cache_ptr->skip_dxpl_id_checks = FALSE;
+ cache_ptr->prefix[0] = '\0'; /* empty string */
+
+ /* Set return value */
+ ret_value = cache_ptr;
+
+done:
+
+ if ( ret_value == 0 ) {
+
+ if ( cache_ptr != NULL ) {
+
+ if ( cache_ptr->slist_ptr != NULL )
+ H5SL_close(cache_ptr->slist_ptr);
+
+ cache_ptr->magic = 0;
+ H5FL_FREE(H5C2_t, cache_ptr);
+ cache_ptr = NULL;
+
+ } /* end if */
+
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_create() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_def_auto_resize_rpt_fcn
+ *
+ * Purpose: Print results of a automatic cache resize.
+ *
+ * This function should only be used where HDprintf() behaves
+ * well -- i.e. not on Windows.
+ *
+ * Return: void
+ *
+ * Programmer: John Mainzer
+ * 10/27/04
+ *
+ * Modifications:
+ *
+ * JRM -- 11/22/04
+ * Reworked function to adapt it to the addition of the
+ * ageout method of cache size reduction.
+ *
+ * JRM -- 1/19/06
+ * Updated function for display the new prefix field of
+ * H5C2_t in output.
+ *
+ *-------------------------------------------------------------------------
+ */
+void
+H5C2_def_auto_resize_rpt_fcn(H5C2_t * cache_ptr,
+#ifndef NDEBUG
+ int32_t version,
+#else /* NDEBUG */
+ int32_t UNUSED version,
+#endif /* NDEBUG */
+ double hit_rate,
+ enum H5C2_resize_status status,
+ size_t old_max_cache_size,
+ size_t new_max_cache_size,
+ size_t old_min_clean_size,
+ size_t new_min_clean_size)
+{
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( version == H5C2__CURR_AUTO_RESIZE_RPT_FCN_VER );
+
+ switch ( status )
+ {
+ case in_spec2:
+ HDfprintf(stdout,
+ "%sAuto cache resize -- no change. (hit rate = %lf)\n",
+ cache_ptr->prefix, hit_rate);
+ break;
+
+ case increase2:
+ HDassert( hit_rate < (cache_ptr->resize_ctl).lower_hr_threshold );
+ HDassert( old_max_cache_size < new_max_cache_size );
+
+ HDfprintf(stdout,
+ "%sAuto cache resize -- hit rate (%lf) out of bounds low (%6.5lf).\n",
+ cache_ptr->prefix, hit_rate,
+ (cache_ptr->resize_ctl).lower_hr_threshold);
+
+ HDfprintf(stdout,
+ "%s cache size increased from (%Zu/%Zu) to (%Zu/%Zu).\n",
+ cache_ptr->prefix,
+ old_max_cache_size,
+ old_min_clean_size,
+ new_max_cache_size,
+ new_min_clean_size);
+ break;
+
+ case decrease2:
+ HDassert( old_max_cache_size > new_max_cache_size );
+
+ switch ( (cache_ptr->resize_ctl).decr_mode )
+ {
+ case H5C2_decr__threshold:
+ HDassert( hit_rate >
+ (cache_ptr->resize_ctl).upper_hr_threshold );
+
+ HDfprintf(stdout,
+ "%sAuto cache resize -- decrease by threshold. HR = %lf > %6.5lf\n",
+ cache_ptr->prefix, hit_rate,
+ (cache_ptr->resize_ctl).upper_hr_threshold);
+
+ HDfprintf(stdout, "%sout of bounds high (%6.5lf).\n",
+ cache_ptr->prefix,
+ (cache_ptr->resize_ctl).upper_hr_threshold);
+ break;
+
+ case H5C2_decr__age_out:
+ HDfprintf(stdout,
+ "%sAuto cache resize -- decrease by ageout. HR = %lf\n",
+ cache_ptr->prefix, hit_rate);
+ break;
+
+ case H5C2_decr__age_out_with_threshold:
+ HDassert( hit_rate >
+ (cache_ptr->resize_ctl).upper_hr_threshold );
+
+ HDfprintf(stdout,
+ "%sAuto cache resize -- decrease by ageout with threshold. HR = %lf > %6.5lf\n",
+ cache_ptr->prefix, hit_rate,
+ (cache_ptr->resize_ctl).upper_hr_threshold);
+ break;
+
+ default:
+ HDfprintf(stdout,
+ "%sAuto cache resize -- decrease by unknown mode. HR = %lf\n",
+ cache_ptr->prefix, hit_rate);
+ }
+
+ HDfprintf(stdout,
+ "%s cache size decreased from (%Zu/%Zu) to (%Zu/%Zu).\n",
+ cache_ptr->prefix,
+ old_max_cache_size,
+ old_min_clean_size,
+ new_max_cache_size,
+ new_min_clean_size);
+ break;
+
+ case at_max_size2:
+ HDfprintf(stdout,
+ "%sAuto cache resize -- hit rate (%lf) out of bounds low (%6.5lf).\n",
+ cache_ptr->prefix, hit_rate,
+ (cache_ptr->resize_ctl).lower_hr_threshold);
+ HDfprintf(stdout,
+ "%s cache already at maximum size so no change.\n",
+ cache_ptr->prefix);
+ break;
+
+ case at_min_size2:
+ HDfprintf(stdout,
+ "%sAuto cache resize -- hit rate (%lf) -- can't decrease.\n",
+ cache_ptr->prefix, hit_rate);
+ HDfprintf(stdout, "%s cache already at minimum size.\n",
+ cache_ptr->prefix);
+ break;
+
+ case increase_disabled2:
+ HDfprintf(stdout,
+ "%sAuto cache resize -- increase disabled -- HR = %lf.",
+ cache_ptr->prefix, hit_rate);
+ break;
+
+ case decrease_disabled2:
+ HDfprintf(stdout,
+ "%sAuto cache resize -- decrease disabled -- HR = %lf.\n",
+ cache_ptr->prefix, hit_rate);
+ break;
+
+ case not_full2:
+ HDassert( hit_rate < (cache_ptr->resize_ctl).lower_hr_threshold );
+
+ HDfprintf(stdout,
+ "%sAuto cache resize -- hit rate (%lf) out of bounds low (%6.5lf).\n",
+ cache_ptr->prefix, hit_rate,
+ (cache_ptr->resize_ctl).lower_hr_threshold);
+ HDfprintf(stdout,
+ "%s cache not full so no increase in size.\n",
+ cache_ptr->prefix);
+ break;
+
+ default:
+ HDfprintf(stdout, "%sAuto cache resize -- unknown status code.\n",
+ cache_ptr->prefix);
+ break;
+ }
+
+ return;
+
+} /* H5C2_def_auto_resize_rpt_fcn() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_dest
+ *
+ * Purpose: Flush all data to disk and destroy the cache.
+ *
+ * This function fails if any object are protected since the
+ * resulting file might not be consistent.
+ *
+ * The primary_dxpl_id and secondary_dxpl_id parameters
+ * specify the dxpl_ids used on the first write occasioned
+ * by the destroy (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.
+ *
+ * Note that *cache_ptr has been freed upon successful return.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/2/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/11/07
+ * Reworked parameter list for the revised cache API.
+ * The function lost its pointer to an instance of
+ * H5F_t (now supplied via cache_ptr), and one of its
+ * dxlp ids.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C2_dest(H5C2_t * cache_ptr,
+ hid_t dxpl_id)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_dest, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ if ( H5C2_flush_cache(cache_ptr, dxpl_id,
+ H5C2__FLUSH_INVALIDATE_FLAG) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache")
+ }
+
+ if ( cache_ptr->slist_ptr != NULL ) {
+
+ H5SL_close(cache_ptr->slist_ptr);
+ cache_ptr->slist_ptr = NULL;
+ }
+
+ cache_ptr->magic = 0;
+
+ H5FL_FREE(H5C2_t, cache_ptr);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_dest() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_dest_empty
+ *
+ * Purpose: Destroy an empty cache.
+ *
+ * This function fails if the cache is not empty on entry.
+ *
+ * Note that *cache_ptr has been freed upon successful return.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/2/04
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C2_dest_empty(H5C2_t * cache_ptr)
+{
+ herr_t ret_value=SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_dest_empty, FAIL)
+
+ /* This would normally be an assert, but we need to use an HGOTO_ERROR
+ * call to shut up the compiler.
+ */
+ if ( ( ! cache_ptr ) ||
+ ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ||
+ ( cache_ptr->index_len != 0 ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Bad cache_ptr or non-empty cache on entry.")
+ }
+
+
+ if ( cache_ptr->slist_ptr != NULL ) {
+
+ H5SL_close(cache_ptr->slist_ptr);
+ cache_ptr->slist_ptr = NULL;
+ }
+
+ cache_ptr->magic = 0;
+
+ H5FL_FREE(H5C2_t, cache_ptr);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_dest_empty() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_expunge_entry
+ *
+ * Purpose: Use this function to tell the cache to expunge an entry
+ * from the cache without writing it to disk even if it is
+ * dirty. The entry may not be either pinned or protected.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/29/06
+ *
+ * Modifications:
+ *
+ * JRM -- 7/11/07
+ * Reworked the parameter list for the revised cache API.
+ * The function lost its file pointer (now passed in the
+ * *cache_ptr), and one of the dxpl ids.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_expunge_entry(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ const H5C2_class_t * type,
+ haddr_t addr)
+{
+ /* const char * fcn_name = "H5C2_expunge_entry()"; */
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5C2_cache_entry_t * entry_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5C2_expunge_entry, FAIL)
+
+ HDassert( H5F_addr_defined(addr) );
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( type );
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ H5C2__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL)
+
+ if ( ( entry_ptr == NULL ) || ( entry_ptr->type != type ) ) {
+
+ /* the target doesn't exist in the cache, so we are done. */
+ HGOTO_DONE(SUCCEED)
+ }
+
+ HDassert( entry_ptr->addr == addr );
+ HDassert( entry_ptr->type == type );
+
+ if ( entry_ptr->is_protected ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, \
+ "Target entry is protected.")
+ }
+
+ if ( entry_ptr->is_pinned ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, \
+ "Target entry is pinned.")
+ }
+
+ /* If we get this far, call H5C2_flush_single_entry() with the
+ * H5C2__FLUSH_INVALIDATE_FLAG and the H5C2__FLUSH_CLEAR_ONLY_FLAG.
+ * This will clear the entry, and then delete it from the cache.
+ */
+
+ result = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ H5C2__FLUSH_INVALIDATE_FLAG |
+ H5C2__FLUSH_CLEAR_ONLY_FLAG,
+ TRUE);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, \
+ "H5C2_flush_single_entry() failed.")
+ }
+
+done:
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_expunge_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_flush_cache
+ *
+ * Purpose: Flush (and possibly destroy) the entries contained in the
+ * specified cache.
+ *
+ * If the cache contains protected entries, the function will
+ * fail, as protected entries cannot be flushed. However
+ * all unprotected entries should be flushed before the
+ * function returns failure.
+ *
+ * The primary_dxpl_id and secondary_dxpl_id parameters
+ * specify the dxpl_ids used on the first write occasioned
+ * by the flush (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.
+ *
+ * Return: Non-negative on success/Negative on failure or if there was
+ * a request to flush all items and something was protected.
+ *
+ * Programmer: John Mainzer
+ * 6/2/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/20/04
+ * Modified the function for the addition of the hash table.
+ *
+ * JRM -- 11/22/04
+ * Added code to remove all epoch markers (if any) from the
+ * LRU list before a destroy. Strictly speaking, this isn't
+ * necessary, as the marker entries reside only in the LRU
+ * list, never in the index or in the tree. However, it
+ * never hurts to tidy up.
+ *
+ * JRM -- 1/6/05
+ * Reworked code to support the new
+ * H5C2__FLUSH_MARKED_ENTRIES_FLAG, and for the replacement of
+ * H5F_FLUSH_INVALIDATE flag with H5C2__FLUSH_INVALIDATE_FLAG.
+ *
+ * Note that the H5C2__FLUSH_INVALIDATE_FLAG takes precidence
+ * over the H5C2__FLUSH_MARKED_ENTRIES_FLAG. Thus if both are
+ * set, the functions behaves as if just the
+ * H5C2__FLUSH_INVALIDATE_FLAG was set.
+ *
+ * The H5C2__FLUSH_CLEAR_ONLY_FLAG flag can co-exist with
+ * either the H5C2__FLUSH_MARKED_ENTRIES_FLAG, or the
+ * H5C2__FLUSH_INVALIDATE_FLAG. In all cases, it is simply
+ * passed along to H5C2_flush_single_entry(). In the case of
+ * H5C2__FLUSH_MARKED_ENTRIES_FLAG, it will only apply to
+ * the marked entries.
+ *
+ * JRM -- 10/15/05
+ * Added code supporting the new
+ * H5C2__FLUSH_IGNORE_PROTECTED_FLAG. We need this flag, as
+ * we now use this function to flush large number of entries
+ * in increasing address order. We do this by marking the
+ * entries to be flushed, calling this function to flush them,
+ * and then restoring LRU order.
+ *
+ * However, it is possible that the cache will contain other,
+ * unmarked protected entries, when we make this call. This
+ * new flag allows us to ignore them.
+ *
+ * Note that even with this flag set, it is still an error
+ * to try to flush a protected entry.
+ *
+ * JRM -- 3/25/06
+ * Updated function to handle pinned entries.
+ *
+ * JRM -- 8/19/06
+ * Added code managing the new flush_in_progress field of
+ * H5C2_t.
+ *
+ * Also reworked function to allow for the possibility that
+ * entries will be dirtied, resized, or renamed during flush
+ * callbacks. As a result, we may have to make multiple
+ * passes through the skip list before the cache is flushed.
+ *
+ * JRM -- 7/11/07
+ * Reworked function to support the new metadata cache API.
+ * The function lost the H5F_t parameter (now passed via
+ * *cache_ptr), and one of the dxpl id parameters.
+ *
+ * JRM -- 10/13/07
+ * Added code to detect and manage the case in which a
+ * serialize callback changes the s-list out from under
+ * the function. The only way I can think of in which this
+ * can happen is if a serialize function loads an entry
+ * into the cache that isn't there already. Quincey tells
+ * me that this will never happen, but I'm not sure I
+ * believe him.
+ *
+ * Note that this is a pretty bad scenario if it ever
+ * happens. The code I have added should allow us to
+ * handle the situation, but one can argue that I should
+ * just scream and die if I ever detect the condidtion.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C2_flush_cache(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ unsigned flags)
+{
+ /* const char * fcn_name = "H5C2_flush_cache()"; */
+ herr_t status;
+ herr_t ret_value = SUCCEED;
+ hbool_t destroy;
+ hbool_t flushed_entries_last_pass;
+ hbool_t flush_marked_entries;
+ hbool_t ignore_protected;
+ hbool_t tried_to_flush_protected_entry = FALSE;
+ int32_t passes = 0;
+ int32_t protected_entries = 0;
+ H5SL_node_t * node_ptr = NULL;
+ H5C2_cache_entry_t * entry_ptr = NULL;
+ H5C2_cache_entry_t * next_entry_ptr = NULL;
+#if H5C2_DO_SANITY_CHECKS
+ int64_t flushed_entries_count;
+ size_t flushed_entries_size;
+ int64_t initial_slist_len;
+ size_t initial_slist_size;
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ FUNC_ENTER_NOAPI(H5C2_flush_cache, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( cache_ptr->skip_file_checks || cache_ptr->f );
+ HDassert( cache_ptr->slist_ptr );
+
+ ignore_protected = ( (flags & H5C2__FLUSH_IGNORE_PROTECTED_FLAG) != 0 );
+
+ destroy = ( (flags & H5C2__FLUSH_INVALIDATE_FLAG) != 0 );
+
+ /* note that flush_marked_entries is set to FALSE if destroy is TRUE */
+ flush_marked_entries = ( ( (flags & H5C2__FLUSH_MARKED_ENTRIES_FLAG) != 0 )
+ &&
+ ( ! destroy )
+ );
+
+ HDassert( ! ( destroy && ignore_protected ) );
+
+ HDassert( ! ( cache_ptr->flush_in_progress ) );
+
+ cache_ptr->flush_in_progress = TRUE;
+
+ if ( destroy ) {
+
+ status = H5C2_flush_invalidate_cache(dxpl_id,
+ cache_ptr,
+ flags);
+
+ if ( status < 0 ) {
+
+ /* This shouldn't happen -- if it does, we are toast so
+ * just scream and die.
+ */
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "flush invalidate failed.")
+ }
+ } else {
+ /* When we are only flushing marked entries, the slist will usually
+ * still contain entries when we have flushed everything we should.
+ * Thus we track whether we have flushed any entries in the last
+ * pass, and terminate if we haven't.
+ */
+
+ flushed_entries_last_pass = TRUE;
+
+ while ( ( passes < H5C2__MAX_PASSES_ON_FLUSH ) &&
+ ( cache_ptr->slist_len != 0 ) &&
+ ( protected_entries == 0 ) &&
+ ( flushed_entries_last_pass ) )
+ {
+ flushed_entries_last_pass = FALSE;
+ node_ptr = H5SL_first(cache_ptr->slist_ptr);
+ if ( node_ptr != NULL ) {
+ next_entry_ptr = (H5C2_cache_entry_t *)H5SL_item(node_ptr);
+
+ if ( next_entry_ptr == NULL ) {
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "next_entry_ptr == NULL 1 ?!?!");
+ }
+ HDassert( next_entry_ptr->magic ==
+ H5C2__H5C2_CACHE_ENTRY_T_MAGIC );
+ HDassert( next_entry_ptr->is_dirty );
+ HDassert( next_entry_ptr->in_slist );
+ } else {
+ next_entry_ptr = NULL;
+ }
+
+ HDassert( node_ptr != NULL );
+
+#if H5C2_DO_SANITY_CHECKS
+ /* For sanity checking, try to verify that the skip list has
+ * the expected size and number of entries at the end of each
+ * internal while loop (see below).
+ *
+ * Doing this get a bit tricky, as depending on flags, we may
+ * or may not flush all the entries in the slist.
+ *
+ * To make things more entertaining, with the advent of the
+ * fractal heap, the entry serialize callback can cause entries
+ * to be dirtied, resized, and/or renamed.
+ *
+ * To deal with this, we first make note of the initial
+ * skip list length and size:
+ */
+ initial_slist_len = cache_ptr->slist_len;
+ initial_slist_size = cache_ptr->slist_size;
+
+ /* We then zero counters that we use to track the number
+ * and total size of entries flushed:
+ */
+ flushed_entries_count = 0;
+ flushed_entries_size = 0;
+
+ /* As mentioned above, there is the possibility that
+ * entries will be dirtied, resized, and/or flushed during
+ * our pass through the skip list. To capture the number
+ * of entries added, and the skip list size delta,
+ * zero the slist_len_increase and slist_size_increase of
+ * the cache's instance of H5C2_t. These fields will be
+ * updated elsewhere to account for slist insertions and/or
+ * dirty entry size changes.
+ */
+ cache_ptr->slist_len_increase = 0;
+ cache_ptr->slist_size_increase = 0;
+
+ /* at the end of the loop, use these values to compute the
+ * expected slist length and size and compare this with the
+ * value recorded in the cache's instance of H5C2_t.
+ */
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ while ( node_ptr != NULL )
+ {
+ entry_ptr = next_entry_ptr;
+
+ /* With the advent of the fractal heap, it is possible
+ * that the serialize 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 serialize 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.
+ */
+
+ if ( entry_ptr->magic != H5C2__H5C2_CACHE_ENTRY_T_MAGIC ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "entry_ptr->magic invalid ?!?!");
+
+ } else if ( ( ! entry_ptr->is_dirty ) ||
+ ( ! entry_ptr->in_slist ) ) {
+
+ /* the s-list has been modified out from under us.
+ * set node_ptr to NULL and break out of the loop.
+ */
+ node_ptr = NULL;
+ break;
+ }
+
+ /* increment node pointer now, before we delete its target
+ * from the slist.
+ */
+ node_ptr = H5SL_next(node_ptr);
+ if ( node_ptr != NULL ) {
+ next_entry_ptr = (H5C2_cache_entry_t *)H5SL_item(node_ptr);
+
+ if ( next_entry_ptr == NULL ) {
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "next_entry_ptr == NULL 2 ?!?!");
+ }
+ HDassert( next_entry_ptr->magic ==
+ H5C2__H5C2_CACHE_ENTRY_T_MAGIC );
+ HDassert( next_entry_ptr->is_dirty );
+ HDassert( next_entry_ptr->in_slist );
+ } else {
+ next_entry_ptr = NULL;
+ }
+
+ HDassert( entry_ptr != NULL );
+ HDassert( entry_ptr->in_slist );
+
+ if ( ( ! flush_marked_entries ) ||
+ ( entry_ptr->flush_marker ) ) {
+
+ if ( entry_ptr->is_protected ) {
+
+ /* we probably have major problems -- but lets flush
+ * everything we can before we decide whether to flag
+ * an error.
+ */
+ tried_to_flush_protected_entry = TRUE;
+ protected_entries++;
+
+ } else if ( entry_ptr->is_pinned ) {
+
+ /* Test to see if we are can flush the entry now.
+ * If we can, go ahead and flush. Note that we
+ * aren't trying to do a destroy here, so that
+ * is not an issue.
+ */
+ if ( TRUE ) { /* When we get to multithreaded cache,
+ * we will need either locking code,
+ * and/or a test to see if the entry
+ * is in flushable condition here.
+ */
+#if H5C2_DO_SANITY_CHECKS
+ flushed_entries_count++;
+ flushed_entries_size += entry_ptr->size;
+#endif /* H5C2_DO_SANITY_CHECKS */
+ status = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ flags,
+ FALSE);
+ if ( status < 0 ) {
+
+ /* This shouldn't happen -- if it does, we
+ * are toast so just scream and die.
+ */
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "dirty pinned entry flush failed.")
+ }
+ }
+ } else {
+#if H5C2_DO_SANITY_CHECKS
+ flushed_entries_count++;
+ flushed_entries_size += entry_ptr->size;
+#endif /* H5C2_DO_SANITY_CHECKS */
+ status = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ flags,
+ FALSE);
+ if ( status < 0 ) {
+
+ /* This shouldn't happen -- if it does, we are
+ * toast so just scream and die.
+ */
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "Can't flush entry.")
+ }
+ flushed_entries_last_pass = TRUE;
+ }
+ }
+ } /* while ( node_ptr != NULL ) */
+
+#if H5C2_DO_SANITY_CHECKS
+ /* Verify that the slist size and length are as expected. */
+
+ HDassert( (initial_slist_len + cache_ptr->slist_len_increase -
+ flushed_entries_count) == cache_ptr->slist_len );
+ HDassert( (initial_slist_size + cache_ptr->slist_size_increase -
+ flushed_entries_size) == cache_ptr->slist_size );
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ passes++;
+
+ } /* while */
+
+ HDassert( protected_entries <= cache_ptr->pl_len );
+
+ if ( ( ( cache_ptr->pl_len > 0 ) && ( !ignore_protected ) )
+ ||
+ ( tried_to_flush_protected_entry ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "cache has protected items")
+ }
+
+ if ( ( cache_ptr->slist_len != 0 ) &&
+ ( passes >= H5C2__MAX_PASSES_ON_FLUSH ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "flush pass limit exceeded.")
+ }
+
+#if H5C2_DO_SANITY_CHECKS
+ if ( ! flush_marked_entries ) {
+
+ HDassert( cache_ptr->slist_len == 0 );
+ HDassert( cache_ptr->slist_size == 0 );
+ }
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ }
+
+done:
+
+ cache_ptr->flush_in_progress = FALSE;
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_flush_cache() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_flush_to_min_clean
+ *
+ * Purpose: Flush dirty entries until the caches min clean size is
+ * attained.
+ *
+ * This function is used in the implementation of the
+ * metadata cache in PHDF5. To avoid "messages from the
+ * future", the cache on process 0 can't be allowed to
+ * flush entries until the other processes have reached
+ * the same point in the calculation. If this constraint
+ * is not met, it is possible that the other processes will
+ * read metadata generated at a future point in the
+ * computation.
+ *
+ *
+ * Return: Non-negative on success/Negative on failure or if
+ * write is not permitted.
+ *
+ * Programmer: John Mainzer
+ * 9/16/05
+ *
+ * Modifications:
+ *
+ * Re-wrote function to flush dirty entries in increasing
+ * address order, while maintaining LRU order in the LRU list
+ * upon return.
+ *
+ * Do this by scanning up the dirty LRU list for entries to
+ * flush to reach min clean size, setting their flush_marker
+ * flags, and recording their addresses in the order
+ * encountered.
+ *
+ * Then call H5C2_flush_cache() to flush the marked entries.
+ *
+ * Finally, use the list of marked entries to force the
+ * correct LRU list order after the flush.
+ *
+ * JRM - 10/13/05
+ *
+ * This change had the oposite of the desired effect. Lets
+ * leave it in (albeit commented out for now). If we can't
+ * find a case where it helps, lets get rid of it.
+ *
+ *
+ * Added some sanity checks to the change which verify the
+ * expected values of the new is_read_only and ro_ref_count
+ * fields.
+ * JRM - 3/29/07
+ *
+ * Modified parameter list for the new metadata cache API.
+ * THe function lost its H5F_t parameter (now passed via
+ * *cache_ptr), and one of its dxpl ids.
+ *
+ * JRM - 7/11/07
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C2_flush_to_min_clean(H5C2_t * cache_ptr,
+ hid_t dxpl_id)
+{
+ herr_t result;
+ herr_t ret_value = SUCCEED;
+ hbool_t write_permitted;
+#if 0 /* modified code -- commented out for now */
+ int i;
+ int flushed_entries_count = 0;
+ size_t flushed_entries_size = 0;
+ size_t space_needed = 0;
+ haddr_t * flushed_entries_list = NULL;
+ H5C2_cache_entry_t * entry_ptr = NULL;
+#endif /* JRM */
+
+ FUNC_ENTER_NOAPI(H5C2_flush_to_min_clean, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( cache_ptr->skip_file_checks || cache_ptr->f );
+
+ if ( cache_ptr->check_write_permitted != NULL ) {
+
+ result = (cache_ptr->check_write_permitted)(cache_ptr->f,
+ dxpl_id,
+ &write_permitted);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Can't get write_permitted")
+ }
+ } else {
+
+ write_permitted = cache_ptr->write_permitted;
+ }
+
+ if ( ! write_permitted ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "cache write is not permitted!?!\n");
+ }
+#if 1 /* original code */
+ result = H5C2_make_space_in_cache(dxpl_id,
+ cache_ptr,
+ (size_t)0,
+ write_permitted);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_make_space_in_cache failed.")
+ }
+#else /* modified code -- commented out for now */
+ if ( cache_ptr->max_cache_size > cache_ptr->index_size ) {
+
+ if ( ((cache_ptr->max_cache_size - cache_ptr->index_size) +
+ cache_ptr->cLRU_list_size) >= cache_ptr->min_clean_size ) {
+
+ space_needed = 0;
+
+ } else {
+
+ space_needed = cache_ptr->min_clean_size -
+ ((cache_ptr->max_cache_size - cache_ptr->index_size) +
+ cache_ptr->cLRU_list_size);
+ }
+ } else {
+
+ if ( cache_ptr->min_clean_size <= cache_ptr->cLRU_list_size ) {
+
+ space_needed = 0;
+
+ } else {
+
+ space_needed = cache_ptr->min_clean_size -
+ cache_ptr->cLRU_list_size;
+ }
+ }
+
+ if ( space_needed > 0 ) { /* we have work to do */
+
+ HDassert( cache_ptr->slist_len > 0 );
+
+ /* allocate an array to keep a list of the entries that we
+ * mark for flush. We need this list to touch up the LRU
+ * list after the flush.
+ */
+ flushed_entries_list = (haddr_t *)H5MM_malloc(sizeof(haddr_t) *
+ (size_t)(cache_ptr->slist_len));
+
+ if ( flushed_entries_list == NULL ) {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "memory allocation failed for flushed entries list")
+ }
+
+ /* Scan the dirty LRU list from tail forward and mark sufficient
+ * entries to free up the necessary space. Keep a list of the
+ * entries marked in the order in which they are encountered.
+ */
+ entry_ptr = cache_ptr->dLRU_tail_ptr;
+
+ while ( ( flushed_entries_size < space_needed ) &&
+ ( flushed_entries_count < cache_ptr->slist_len ) &&
+ ( entry_ptr != NULL ) )
+ {
+ HDassert( ! (entry_ptr->is_protected) );
+ HDassert( ! (entry_ptr->is_read_only) );
+ HDassert( entry_ptr->ro_ref_count == 0 );
+ HDassert( entry_ptr->is_dirty );
+ HDassert( entry_ptr->in_slist );
+
+ entry_ptr->flush_marker = TRUE;
+ flushed_entries_size += entry_ptr->size;
+ flushed_entries_list[flushed_entries_count] = entry_ptr->addr;
+ flushed_entries_count++;
+ entry_ptr = entry_ptr->aux_prev;
+ }
+
+ if ( ( flushed_entries_count > cache_ptr->slist_len) ||
+ ( flushed_entries_size < space_needed ) ) {
+ HDfprintf(stdout, "flushed_entries_count = %d <= %d = slist_size\n",
+ (int)flushed_entries_count, (int)(cache_ptr->slist_size));
+ HDfprintf(stdout,
+ "flushed_entries_size = %d < %d = space_needed.\n",
+ (int)flushed_entries_size, (int)space_needed);
+ }
+
+ HDassert( flushed_entries_count <= cache_ptr->slist_len );
+ HDassert( flushed_entries_size >= space_needed );
+
+
+ /* Flush the marked entries */
+ result = H5C2_flush_cache(f, primary_dxpl_id, secondary_dxpl_id,
+ cache_ptr, H5C2__FLUSH_MARKED_ENTRIES_FLAG |
+ H5C2__FLUSH_IGNORE_PROTECTED_FLAG);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C2_flush_cache failed.")
+ }
+
+ /* Now touch up the LRU list so as to place the flushed entries in
+ * the order they they would be in if we had flushed them in the
+ * order we encountered them in.
+ */
+
+ i = 0;
+ while ( i < flushed_entries_count )
+ {
+ H5C2__SEARCH_INDEX_NO_STATS(cache_ptr, flushed_entries_list[i], \
+ entry_ptr, FAIL)
+
+ /* At present, the above search must always succeed. However,
+ * that may change. Write the code so we need only remove the
+ * following assert in that event.
+ */
+ HDassert( entry_ptr != NULL );
+ H5C2__FAKE_RP_FOR_MOST_RECENT_ACCESS(cache_ptr, entry_ptr, FAIL)
+ i++;
+ }
+ } /* if ( space_needed > 0 ) */
+#endif /* end modified code -- commented out for now */
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_flush_to_min_clean() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_get_cache_auto_resize_config
+ *
+ * Purpose: Copy the current configuration of the cache automatic
+ * re-sizing function into the instance of H5C2_auto_size_ctl_t
+ * pointed to by config_ptr.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 10/8/04
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_get_cache_auto_resize_config(H5C2_t * cache_ptr,
+ H5C2_auto_size_ctl_t *config_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_get_cache_auto_resize_config, FAIL)
+
+ if ( ( cache_ptr == NULL ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry.")
+ }
+
+ if ( config_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad config_ptr on entry.")
+ }
+
+ *config_ptr = cache_ptr->resize_ctl;
+
+ config_ptr->set_initial_size = FALSE;
+ config_ptr->initial_size = cache_ptr->max_cache_size;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_get_cache_auto_resize_config() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_get_cache_size
+ *
+ * Purpose: Return the cache maximum size, the minimum clean size, the
+ * current size, and the current number of entries in
+ * *max_size_ptr, *min_clean_size_ptr, *cur_size_ptr, and
+ * *cur_num_entries_ptr respectively. If any of these
+ * parameters are NULL, skip that value.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 10/8/04
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_get_cache_size(H5C2_t * cache_ptr,
+ size_t * max_size_ptr,
+ size_t * min_clean_size_ptr,
+ size_t * cur_size_ptr,
+ int32_t * cur_num_entries_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_get_cache_size, FAIL)
+
+ if ( ( cache_ptr == NULL ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry.")
+ }
+
+ if ( max_size_ptr != NULL ) {
+
+ *max_size_ptr = cache_ptr->max_cache_size;
+ }
+
+ if ( min_clean_size_ptr != NULL ) {
+
+ *min_clean_size_ptr = cache_ptr->min_clean_size;
+ }
+
+ if ( cur_size_ptr != NULL ) {
+
+ *cur_size_ptr = cache_ptr->index_size;
+ }
+
+ if ( cur_num_entries_ptr != NULL ) {
+
+ *cur_num_entries_ptr = cache_ptr->index_len;
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_get_cache_size() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_get_cache_hit_rate
+ *
+ * Purpose: Compute and return the current cache hit rate in
+ * *hit_rate_ptr. If there have been no accesses since the
+ * last time the cache hit rate stats were reset, set
+ * *hit_rate_ptr to 0.0. On error, *hit_rate_ptr is
+ * undefined.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 10/7/04
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_get_cache_hit_rate(H5C2_t * cache_ptr,
+ double * hit_rate_ptr)
+
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_get_cache_hit_rate, FAIL)
+
+ if ( ( cache_ptr == NULL ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry.")
+ }
+
+ if ( hit_rate_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad hit_rate_ptr on entry.")
+ }
+
+ HDassert( cache_ptr->cache_hits >= 0 );
+ HDassert( cache_ptr->cache_accesses >= cache_ptr->cache_hits );
+
+ if ( cache_ptr->cache_accesses > 0 ) {
+
+ *hit_rate_ptr = ((double)(cache_ptr->cache_hits)) /
+ ((double)(cache_ptr->cache_accesses));
+
+ } else {
+
+ *hit_rate_ptr = 0.0;
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_get_cache_hit_rate() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_get_entry_status
+ *
+ * Purpose: This function is used to determine whether the cache
+ * contains an entry with the specified base address. If
+ * the entry exists, it also reports some status information
+ * on the entry.
+ *
+ * Status information is reported in the locations pointed
+ * to by the size_ptr, in_cache_ptr, is_dirty_ptr, and
+ * is_protected_ptr. While in_cache_ptr must be defined,
+ * the remaining pointers may be NULL, in which case the
+ * associated data is not reported.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 7/1/05
+ *
+ * Modifications:
+ *
+ * JRM -- 4/26/06
+ * Added the is_pinned_ptr parameter and supporting code.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_get_entry_status(H5C2_t * cache_ptr,
+ haddr_t addr,
+ size_t * size_ptr,
+ hbool_t * in_cache_ptr,
+ hbool_t * is_dirty_ptr,
+ hbool_t * is_protected_ptr,
+ hbool_t * is_pinned_ptr)
+{
+ /* const char * fcn_name = "H5C2_get_entry_status()"; */
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5C2_cache_entry_t * entry_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5C2_get_entry_status, FAIL)
+
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( H5F_addr_defined(addr) );
+ HDassert( in_cache_ptr != NULL );
+
+ /* this test duplicates tow of the above asserts, but we need an
+ * invocation of HGOTO_ERROR to keep the compiler happy.
+ */
+ if ( ( cache_ptr == NULL ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry.")
+ }
+
+ H5C2__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL)
+
+ if ( entry_ptr == NULL ) {
+
+ /* the entry doesn't exist in the cache -- report this
+ * and quit.
+ */
+ *in_cache_ptr = FALSE;
+
+ } else {
+
+ *in_cache_ptr = TRUE;
+
+ if ( size_ptr != NULL ) {
+
+ *size_ptr = entry_ptr->size;
+ }
+
+ if ( is_dirty_ptr != NULL ) {
+
+ *is_dirty_ptr = entry_ptr->is_dirty;
+ }
+
+ if ( is_protected_ptr != NULL ) {
+
+ *is_protected_ptr = entry_ptr->is_protected;
+ }
+
+ if ( is_pinned_ptr != NULL ) {
+
+ *is_pinned_ptr = entry_ptr->is_pinned;
+ }
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_get_entry_status() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_get_evictions_enabled()
+ *
+ * Purpose: Copy the current value of cache_ptr->evictions_enabled into
+ * *evictions_enabled_ptr.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 7/27/07
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_get_evictions_enabled(H5C2_t * cache_ptr,
+ hbool_t * evictions_enabled_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_get_evictions_enabled, FAIL)
+
+ if ( ( cache_ptr == NULL ) ||
+ ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry.")
+ }
+
+ if ( evictions_enabled_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Bad evictions_enabled_ptr on entry.")
+ }
+
+ *evictions_enabled_ptr = cache_ptr->evictions_enabled;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_get_evictions_enabled() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_get_trace_file_ptr
+ *
+ * Purpose: Get the trace_file_ptr field from the cache.
+ *
+ * This field will either be NULL (which indicates that trace
+ * file logging is turned off), or contain a pointer to the
+ * open file to which trace file data is to be written.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 1/20/06
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_get_trace_file_ptr(H5C2_t * cache_ptr,
+ FILE ** trace_file_ptr_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_get_trace_file_ptr, FAIL)
+
+ /* This would normally be an assert, but we need to use an HGOTO_ERROR
+ * call to shut up the compiler.
+ */
+ if ( ( ! cache_ptr ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr")
+ }
+
+ if ( trace_file_ptr_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "NULL trace_file_ptr_ptr")
+ }
+
+ *trace_file_ptr_ptr = cache_ptr->trace_file_ptr;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_get_trace_file_ptr() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_insert_entry
+ *
+ * Purpose: Adds the specified thing to the cache. The thing need not
+ * exist on disk yet, but it must have an address and disk
+ * space reserved.
+ *
+ * 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.
+ *
+ * The primary_dxpl_id is the dxpl_id passed to the
+ * check_write_permitted function if such a function has been
+ * provided.
+ *
+ * Observe that this function cannot occasion a read.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/2/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/21/04
+ * Updated function for the addition of the hash table.
+ *
+ * JRM -- 10/28/04
+ * Added code to set the cache_full flag to TRUE when ever
+ * we need to make space in the cache.
+ *
+ * JRM -- 11/22/04
+ * Updated function for the addition of the first_flush_ptr
+ * parameter to H5C2_make_space_in_cache().
+ *
+ * JRM -- 1/6/05
+ * Added the flags parameter, and code supporting
+ * H5C2__SET_FLUSH_MARKER_FLAG. Note that this flag is
+ * ignored unless the new entry is dirty.
+ *
+ * JRM -- 6/6/05
+ * Added code to force all inserted entries to be dirty.
+ * This is part of a set of changes moving management of the
+ * is_dirty field of H5C2_cache_entry_t into the H5C2 code.
+ *
+ * JRM -- 6/24/05
+ * Added support for the new write_permitted field of
+ * the H5C2_t structure.
+ *
+ * JRM -- 3/16/06
+ * Added initialization for the new is_pinned field of the
+ * H5C2_cache_entry_t structure.
+ *
+ * JRM -- 5/3/06
+ * Added initialization for the new dirtied field of the
+ * H5C2_cache_entry_t structure.
+ *
+ * JRM -- 8/9/06
+ * Added code supporting insertion of pinned entries.
+ *
+ * JRM -- 8/21/06
+ * Added initialization for the new flush_in_progress and
+ * destroy_in_progress fields.
+ *
+ * JRM -- 3/29/07
+ * Added initialization for the new is_read_only and
+ * ro_ref_count fields.
+ *
+ * JRM -- 7/11/07
+ * Reworked the parameter list for the revised metadata
+ * cache API. The function lost its pointer to H5F_t
+ * (now supplied via *cache_ptr), and one of dxpl id
+ * parameters. It gained a entry length parameter.
+ * Numerous internal changes to support the API change.
+ *
+ * JRM -- 8/1/07
+ * Added code to disable evictions when the new
+ * evictions_enabled field is FALSE.
+ *
+ * JRM -- 10/12/07
+ * Added initialization for the new magic field.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_insert_entry(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ const H5C2_class_t * type,
+ haddr_t addr,
+ size_t len,
+ void * thing,
+ unsigned int flags)
+{
+ herr_t result;
+ herr_t ret_value = SUCCEED; /* Return value */
+ hbool_t insert_pinned;
+ hbool_t set_flush_marker;
+ hbool_t write_permitted = TRUE;
+ H5C2_cache_entry_t * entry_ptr;
+ H5C2_cache_entry_t * test_entry_ptr;
+
+ FUNC_ENTER_NOAPI(H5C2_insert_entry, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( cache_ptr->skip_file_checks || cache_ptr->f );
+ HDassert( type );
+ HDassert( H5F_addr_defined(addr) );
+ HDassert( thing );
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_verify_not_in_index(cache_ptr, (H5C2_cache_entry_t *)thing) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "thing already in index.\n");
+ }
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ set_flush_marker = ( (flags & H5C2__SET_FLUSH_MARKER_FLAG) != 0 );
+ insert_pinned = ( (flags & H5C2__PIN_ENTRY_FLAG) != 0 );
+
+ entry_ptr = (H5C2_cache_entry_t *)thing;
+
+ entry_ptr->magic = H5C2__H5C2_CACHE_ENTRY_T_MAGIC;
+
+ entry_ptr->addr = addr;
+ entry_ptr->type = type;
+
+ /* newly inserted entries are assumed to be dirty */
+ entry_ptr->is_dirty = TRUE;
+
+ /* not protected, so can't be dirtied */
+ entry_ptr->dirtied = FALSE;
+
+ entry_ptr->size = len;
+
+ HDassert( entry_ptr->size < H5C2_MAX_ENTRY_SIZE );
+
+ entry_ptr->in_slist = FALSE;
+
+#ifdef H5_HAVE_PARALLEL
+ entry_ptr->clear_on_unprotect = FALSE;
+#endif /* H5_HAVE_PARALLEL */
+
+ entry_ptr->flush_in_progress = FALSE;
+ entry_ptr->destroy_in_progress = FALSE;
+
+ entry_ptr->ht_next = NULL;
+ entry_ptr->ht_prev = NULL;
+
+ entry_ptr->next = NULL;
+ entry_ptr->prev = NULL;
+
+ entry_ptr->aux_next = NULL;
+ entry_ptr->aux_prev = NULL;
+
+ H5C2__RESET_CACHE_ENTRY_STATS(entry_ptr)
+
+ if ( ( cache_ptr->evictions_enabled ) &&
+ ( (cache_ptr->index_size + entry_ptr->size) >
+ cache_ptr->max_cache_size ) ) {
+
+ size_t space_needed;
+
+ cache_ptr->cache_full = TRUE;
+
+ if ( cache_ptr->check_write_permitted != NULL ) {
+
+ result = (cache_ptr->check_write_permitted)(cache_ptr->f,
+ dxpl_id,
+ &write_permitted);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \
+ "Can't get write_permitted")
+ }
+ } else {
+
+ write_permitted = cache_ptr->write_permitted;
+ }
+
+ HDassert( entry_ptr->size <= H5C2_MAX_ENTRY_SIZE );
+
+ space_needed = entry_ptr->size;
+
+ if ( space_needed > cache_ptr->max_cache_size ) {
+
+ space_needed = cache_ptr->max_cache_size;
+ }
+
+ /* Note that space_needed is just the amount of space that
+ * needed to insert the new entry without exceeding the cache
+ * size limit. The subsequent call to H5C2_make_space_in_cache()
+ * may evict the entries required to free more or less space
+ * depending on conditions. It MAY be less if the cache is
+ * currently undersized, or more if the cache is oversized.
+ *
+ * The cache can exceed its maximum size limit via the following
+ * mechanisms:
+ *
+ * First, it is possible for the cache to grow without
+ * bound as long as entries are protected and not unprotected.
+ *
+ * Second, when writes are not permitted it is also possible
+ * for the cache to grow without bound.
+ *
+ * Finally, we usually don't check to see if the cache is
+ * oversized at the end of an unprotect. As a result, it is
+ * possible to have a vastly oversized cache with no protected
+ * entries as long as all the protects preceed the unprotects.
+ *
+ * Since items 1 and 2 are not changing any time soon, I see
+ * no point in worrying about the third.
+ */
+
+ result = H5C2_make_space_in_cache(dxpl_id,
+ cache_ptr,
+ space_needed,
+ write_permitted);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \
+ "H5C2_make_space_in_cache failed.")
+ }
+ }
+
+ /* verify that the new entry isn't already in the hash table -- scream
+ * and die if it is.
+ */
+
+ H5C2__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL)
+
+ if ( test_entry_ptr != NULL ) {
+
+ if ( test_entry_ptr == entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \
+ "entry already in cache.")
+
+ } else {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \
+ "duplicate entry in cache.")
+ }
+ }
+
+ /* we don't initialize the protected field until here as it is
+ * possible that the entry is already in the cache, and already
+ * protected. If it is, we don't want to make things worse by
+ * marking it unprotected.
+ */
+
+ entry_ptr->is_protected = FALSE;
+ entry_ptr->is_read_only = FALSE;
+ entry_ptr->ro_ref_count = 0;
+
+ entry_ptr->is_pinned = insert_pinned;
+
+ H5C2__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL)
+
+ /* New entries are presumed to be dirty, so this if statement is
+ * unnecessary. Rework it once the rest of the code changes are
+ * in and tested. -- JRM
+ */
+ if ( entry_ptr->is_dirty ) {
+
+ entry_ptr->flush_marker = set_flush_marker;
+ H5C2__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
+
+ } else {
+
+ entry_ptr->flush_marker = FALSE;
+ }
+
+ H5C2__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, FAIL)
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ H5C2__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr)
+
+done:
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_insert_entry() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_mark_entries_as_clean
+ *
+ * Purpose: When the H5C2 code is used to implement the metadata caches
+ * in PHDF5, only the cache with MPI_rank 0 is allowed to
+ * actually write entries to disk -- all other caches must
+ * retain dirty entries until they are advised that the
+ * entries are clean.
+ *
+ * This function exists to allow the H5C2 code to receive these
+ * notifications.
+ *
+ * The function receives a list of entry base addresses
+ * which must refer to dirty entries in the cache. If any
+ * of the entries are either clean or don't exist, the
+ * function flags an error.
+ *
+ * The function scans the list of entries and flushes all
+ * those that are currently unprotected with the
+ * H5C2__FLUSH_CLEAR_ONLY_FLAG. Those that are currently
+ * protected are flagged for clearing when they are
+ * unprotected.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 7/5/05
+ *
+ * Modifications:
+ *
+ * Reworked function to flush entries in LRU order instead
+ * of increasing address order. The hope is that this will
+ * improve the hit rate on the slave caches.
+ *
+ * JRM - 10/13/05
+ *
+ * Leave the old code in place for now (commented out) for
+ * benchmarking.
+ *
+ * JRM -- 4/13/06
+ * Updated function to deal with pinned entries.
+ *
+ * JRM -- 7/11/07
+ * Revised function for the new metadata cache API. The
+ * function lost its point to H5F_t (now supplied via
+ * *cache_ptr), and one of its dxpl ids. Also internal
+ * changes supporting the revised API.
+ *
+ * JRM -- 10/13/07
+ * Didn't modify this function to detect the case in which
+ * the LRU is modified out from under it. It shouldn't be
+ * an issue here, as this function is only called in the
+ * parallel case, and serialize callbacks must not modify
+ * other entries in parallel case. If they do, they will
+ * cause inconsistancies in metadata across processes.
+ *
+ * Further, since this function only clears entries, and
+ * thus the serialize functions are never called, the
+ * situation will be even worse, as the changes will only
+ * exist on process 0.
+ *
+ * Bottom line -- the calls to H5C2_flush_single_entry()
+ * in this function will not call serialize, thus no change
+ * in the LRU is possible. Even if they did, the serialize()
+ * callbacks are banned from making such changes in the
+ * parallel case.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef H5_HAVE_PARALLEL
+herr_t
+H5C2_mark_entries_as_clean(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ int32_t ce_array_len,
+ haddr_t * ce_array_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ int entries_cleared;
+ int entries_examined;
+ int i;
+ int initial_list_len;
+ haddr_t addr;
+#if H5C2_DO_SANITY_CHECKS
+ int pinned_entries_marked = 0;
+ int protected_entries_marked = 0;
+ int other_entries_marked = 0;
+ haddr_t last_addr;
+#endif /* H5C2_DO_SANITY_CHECKS */
+ H5C2_cache_entry_t * clear_ptr = NULL;
+ H5C2_cache_entry_t * entry_ptr = NULL;
+
+ FUNC_ENTER_NOAPI(H5C2_mark_entries_as_clean, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ HDassert( ce_array_len > 0 );
+ HDassert( ce_array_ptr != NULL );
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HDassert(0);
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ for ( i = 0; i < ce_array_len; i++ )
+ {
+ addr = ce_array_ptr[i];
+
+#if H5C2_DO_SANITY_CHECKS
+ if ( i == 0 ) {
+
+ last_addr = addr;
+
+ } else {
+
+ if ( last_addr == addr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Duplicate entry in cleaned list.\n");
+
+ } else if ( last_addr > addr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "cleaned list not sorted.\n");
+ }
+ }
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HDassert(0);
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ HDassert( H5F_addr_defined(addr) );
+
+ H5C2__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL)
+
+ if ( entry_ptr == NULL ) {
+#if H5C2_DO_SANITY_CHECKS
+ HDfprintf(stdout,
+ "H5C2_mark_entries_as_clean: entry[%d] = %ld not in cache.\n",
+ (int)i,
+ (long)addr);
+#endif /* H5C2_DO_SANITY_CHECKS */
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Listed entry not in cache?!?!?.")
+
+ } else if ( ! entry_ptr->is_dirty ) {
+
+#if H5C2_DO_SANITY_CHECKS
+ HDfprintf(stdout,
+ "H5C2_mark_entries_as_clean: entry %ld is not dirty!?!\n",
+ (long)addr);
+#endif /* H5C2_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 ( H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ addr,
+ H5C2__FLUSH_CLEAR_ONLY_FLAG,
+ 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.
+ */
+ entry_ptr->clear_on_unprotect = TRUE;
+#if H5C2_DO_SANITY_CHECKS
+ if ( entry_ptr->is_protected ) {
+
+ protected_entries_marked++;
+
+ } else if ( entry_ptr->is_pinned ) {
+
+ pinned_entries_marked++;
+
+ } else {
+
+ other_entries_marked++;
+ }
+#endif /* H5C2_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.
+ */
+
+ entries_cleared = 0;
+ entries_examined = 0;
+ initial_list_len = cache_ptr->LRU_list_len;
+ entry_ptr = cache_ptr->LRU_tail_ptr;
+
+ while ( ( entry_ptr != NULL ) &&
+ ( entries_examined <= initial_list_len ) &&
+ ( entries_cleared < ce_array_len ) )
+ {
+ if ( entry_ptr->clear_on_unprotect ) {
+
+ entry_ptr->clear_on_unprotect = FALSE;
+ clear_ptr = entry_ptr;
+ entry_ptr = entry_ptr->prev;
+ entries_cleared++;
+
+ if ( H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ clear_ptr->type,
+ clear_ptr->addr,
+ H5C2__FLUSH_CLEAR_ONLY_FLAG,
+ TRUE) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.")
+ }
+ } else {
+
+ entry_ptr = entry_ptr->prev;
+ }
+ entries_examined++;
+ }
+
+#if H5C2_DO_SANITY_CHECKS
+ HDassert( entries_cleared == other_entries_marked );
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ /* It is also possible that some of the cleared entries are on the
+ * pinned list. Must scan that also.
+ */
+
+ entry_ptr = cache_ptr->pel_head_ptr;
+
+ while ( entry_ptr != NULL )
+ {
+ if ( entry_ptr->clear_on_unprotect ) {
+
+ entry_ptr->clear_on_unprotect = FALSE;
+ clear_ptr = entry_ptr;
+ entry_ptr = entry_ptr->next;
+ entries_cleared++;
+
+ if ( H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ clear_ptr->type,
+ clear_ptr->addr,
+ H5C2__FLUSH_CLEAR_ONLY_FLAG,
+ TRUE) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.")
+ }
+ } else {
+
+ entry_ptr = entry_ptr->next;
+ }
+ }
+
+#if H5C2_DO_SANITY_CHECKS
+ HDassert( entries_cleared == pinned_entries_marked + other_entries_marked );
+ HDassert( entries_cleared + protected_entries_marked == ce_array_len );
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ HDassert( ( entries_cleared == ce_array_len ) ||
+ ( (ce_array_len - entries_cleared) <= cache_ptr->pl_len ) );
+
+#if H5C2_DO_SANITY_CHECKS
+ i = 0;
+ entry_ptr = cache_ptr->pl_head_ptr;
+ while ( entry_ptr != NULL )
+ {
+ if ( entry_ptr->clear_on_unprotect ) {
+
+ i++;
+ }
+ entry_ptr = entry_ptr->next;
+ }
+ HDassert( (entries_cleared + i) == ce_array_len );
+#endif /* H5C2_DO_SANITY_CHECKS */
+#endif /* modified code */
+
+done:
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HDassert(0);
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_mark_entries_as_clean() */
+#endif /* H5_HAVE_PARALLEL */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_mark_pinned_entry_dirty
+ *
+ * Purpose: Mark a pinned entry as dirty. The target entry MUST be
+ * be pinned, and MUST be unprotected.
+ *
+ * If the entry has changed size, the function updates
+ * data structures for the size change.
+ *
+ * If the entry is not already dirty, the function places
+ * the entry on the skip list.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 3/22/06
+ *
+ * Modifications:
+ *
+ * None
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C2_mark_pinned_entry_dirty(H5C2_t * cache_ptr,
+ void * thing,
+ hbool_t size_changed,
+ size_t new_size)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5C2_cache_entry_t * entry_ptr;
+
+ FUNC_ENTER_NOAPI(H5C2_mark_pinned_entry_dirty, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( thing );
+ HDassert( ( size_changed == TRUE ) || ( size_changed == FALSE ) );
+
+ entry_ptr = (H5C2_cache_entry_t *)thing;
+
+ if ( ! ( entry_ptr->is_pinned ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "Entry isn't pinned??")
+ }
+
+ if ( entry_ptr->is_protected ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "Entry is protected??")
+ }
+
+ /* mark the entry as dirty if it isn't already */
+ entry_ptr->is_dirty = TRUE;
+
+ /* update for change in entry size if necessary */
+ if ( ( size_changed ) && ( entry_ptr->size != new_size ) ) {
+
+ /* update the protected entry list */
+ H5C2__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr->pel_len), \
+ (cache_ptr->pel_size), \
+ (entry_ptr->size), (new_size));
+
+ /* update the hash table */
+ H5C2__UPDATE_INDEX_FOR_SIZE_CHANGE((cache_ptr), (entry_ptr->size),\
+ (new_size));
+
+ /* if the entry is in the skip list, update that too */
+ if ( entry_ptr->in_slist ) {
+
+ H5C2__UPDATE_SLIST_FOR_SIZE_CHANGE((cache_ptr), (entry_ptr->size),\
+ (new_size));
+ }
+
+ /* update statistics just before changing the entry size */
+ H5C2__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE((cache_ptr), (entry_ptr), \
+ (new_size));
+
+ /* finally, update the entry size proper */
+ entry_ptr->size = new_size;
+ }
+
+ if ( ! (entry_ptr->in_slist) ) {
+
+ H5C2__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
+ }
+
+ H5C2__UPDATE_STATS_FOR_DIRTY_PIN(cache_ptr, entry_ptr)
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_mark_pinned_entry_dirty() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_mark_pinned_or_protected_entry_dirty
+ *
+ * Purpose: Mark a pinned or protected entry as dirty. The target entry
+ * MUST be either pinned or protected, and MAY be both.
+ *
+ * At present, this funtion does not support size change.
+ *
+ * In the protected case, this call is the functional
+ * equivalent of setting the H5C2__DIRTIED_FLAG on an unprotect
+ * call.
+ *
+ * In the pinned but not protected case, if the entry is not
+ * already dirty, the function places function marks the entry
+ * dirty and places it on the skip list.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 5/15/06
+ *
+ * Modifications:
+ *
+ * JRM -- 3/29/07
+ * Added sanity check to verify that the pinned entry
+ * is not protected read only.
+ *
+ * This sanity check is commented out for now -- uncomment
+ * it once we deal with the problem of entries being protected
+ * read only, and then dirtied.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C2_mark_pinned_or_protected_entry_dirty(H5C2_t * cache_ptr,
+ void * thing)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5C2_cache_entry_t * entry_ptr;
+
+ FUNC_ENTER_NOAPI(H5C2_mark_pinned_or_protected_entry_dirty, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( thing );
+
+ entry_ptr = (H5C2_cache_entry_t *)thing;
+
+ if ( entry_ptr->is_protected ) {
+#if 1 /* JRM - uncomment this when possible */
+ HDassert( ! ((entry_ptr)->is_read_only) );
+#endif
+ /* set the dirtied flag */
+ entry_ptr->dirtied = TRUE;
+
+ } else if ( entry_ptr->is_pinned ) {
+
+ /* mark the entry as dirty if it isn't already */
+ entry_ptr->is_dirty = TRUE;
+
+
+ if ( ! (entry_ptr->in_slist) ) {
+
+ H5C2__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
+ }
+
+ H5C2__UPDATE_STATS_FOR_DIRTY_PIN(cache_ptr, entry_ptr)
+
+ } else {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "Entry is neither pinned nor protected??")
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_mark_pinned_or_protected_entry_dirty() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_rename_entry
+ *
+ * Purpose: Use this function to notify the cache that an entry's
+ * file address changed.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/2/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/21/04
+ * Updated function for the addition of the hash table.
+ *
+ * JRM -- 6/6/05
+ * Updated function to force all renamed entries to be
+ * dirty. This is part of a series of code modifications
+ * moving management of the is_dirty field of
+ * H5C2_cache_entry_t into the H5C2 code.
+ *
+ * JRM -- 4/3/06
+ * Updated function to disallow renaming of pinned entries.
+ *
+ * JRM -- 4/27/06
+ * Updated function to support renaming of pinned entries.
+ *
+ * JRM -- 8/24/06
+ * Updated function to refrain from alterning the index, the
+ * replacement policy data structures, and skip list when
+ * the function is called within the flush callback for the
+ * target entry and the target entry is being destroyed.
+ *
+ * Note that in this case H5C2_flush_single_entry() will handle
+ * all these details for us.
+ *
+ * JRM -- 10/13/07
+ * Renames of the target entry in a serialize callback are
+ * now handled by H5C2_flush_single_entry() -- hence the above
+ * modification is now obsolete.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_rename_entry(H5C2_t * cache_ptr,
+ const H5C2_class_t * type,
+ haddr_t old_addr,
+ haddr_t new_addr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ hbool_t was_dirty;
+ H5C2_cache_entry_t * entry_ptr = NULL;
+ H5C2_cache_entry_t * test_entry_ptr = NULL;
+#if H5C2_DO_SANITY_CHECKS
+ hbool_t removed_entry_from_slist = FALSE;
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ FUNC_ENTER_NOAPI(H5C2_rename_entry, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( type );
+ HDassert( H5F_addr_defined(old_addr) );
+ HDassert( H5F_addr_defined(new_addr) );
+ HDassert( H5F_addr_ne(old_addr, new_addr) );
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ H5C2__SEARCH_INDEX(cache_ptr, old_addr, entry_ptr, FAIL)
+
+ if ( ( entry_ptr == NULL ) || ( entry_ptr->type != type ) ) {
+
+ /* the old item doesn't exist in the cache, so we are done. */
+ HGOTO_DONE(SUCCEED)
+ }
+
+ HDassert( entry_ptr->addr == old_addr );
+ HDassert( entry_ptr->type == type );
+
+ if ( entry_ptr->is_protected ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \
+ "Target entry is protected.")
+ }
+
+ H5C2__SEARCH_INDEX(cache_ptr, new_addr, test_entry_ptr, FAIL)
+
+ if ( test_entry_ptr != NULL ) { /* we are hosed */
+
+ if ( test_entry_ptr->type == type ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \
+ "Target already renamed & reinserted???.")
+
+ } else {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \
+ "New address already in use?.")
+
+ }
+ }
+
+ /* If we get this far we have work to do. Remove *entry_ptr from
+ * the hash table (and skip list if necessary), change its address to the
+ * new address, mark it as dirty (if it isn't already) and then re-insert.
+ *
+ * Update the replacement policy for a hit to avoid an eviction before
+ * the renamed entry is touched. Update stats for a rename.
+ *
+ * Note that we do not check the size of the cache, or evict anything.
+ * Since this is a simple re-name, cache size should be unaffected.
+ *
+ * Check to see if the target entry is in the process of being destroyed
+ * before we delete from the index, etc. If it is, all we do is
+ * change the addr. If the entry is only in the process of being flushed,
+ * don't mark it as dirty either, lest we confuse the flush call back.
+ */
+
+ if ( ! ( entry_ptr->destroy_in_progress ) ) {
+
+ H5C2__DELETE_FROM_INDEX(cache_ptr, entry_ptr)
+
+ if ( entry_ptr->in_slist ) {
+
+ HDassert( cache_ptr->slist_ptr );
+
+ H5C2__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr)
+
+#if H5C2_DO_SANITY_CHECKS
+
+ removed_entry_from_slist = TRUE;
+
+#endif /* H5C2_DO_SANITY_CHECKS */
+ }
+ }
+
+ entry_ptr->addr = new_addr;
+
+ if ( ! ( entry_ptr->destroy_in_progress ) ) {
+
+ was_dirty = entry_ptr->is_dirty;
+
+ if ( ! ( entry_ptr->flush_in_progress ) ) {
+
+ entry_ptr->is_dirty = TRUE;
+ }
+
+ H5C2__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL)
+
+ if ( ! ( entry_ptr->flush_in_progress ) ) {
+
+ H5C2__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
+
+#if H5C2_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 > entry_ptr->size );
+
+ cache_ptr->slist_len_increase -= 1;
+ cache_ptr->slist_size_increase -= entry_ptr->size;
+ }
+
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ H5C2__UPDATE_RP_FOR_RENAME(cache_ptr, entry_ptr, was_dirty, FAIL)
+ }
+ }
+
+ H5C2__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr)
+
+done:
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_rename_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_resize_pinned_entry
+ *
+ * Purpose: Resize a pinned entry. The target entry MUST be
+ * be pinned, and MUST not be unprotected.
+ *
+ * Resizing an entry dirties it, so if the entry is not
+ * already dirty, the function places the entry on the
+ * skip list.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 7/5/06
+ *
+ * Modifications:
+ *
+ * None
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C2_resize_pinned_entry(H5C2_t * cache_ptr,
+ void * thing,
+ size_t new_size)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5C2_cache_entry_t * entry_ptr;
+
+ FUNC_ENTER_NOAPI(H5C2_resize_pinned_entry, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( thing );
+
+ entry_ptr = (H5C2_cache_entry_t *)thing;
+
+ if ( new_size <= 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTRESIZE, FAIL, \
+ "New size is non-positive.")
+ }
+
+ if ( ! ( entry_ptr->is_pinned ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTRESIZE, FAIL, \
+ "Entry isn't pinned??")
+ }
+
+ if ( entry_ptr->is_protected ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTRESIZE, FAIL, \
+ "Entry is protected??")
+ }
+
+ /* resizing dirties entries -- mark the entry as dirty if it
+ * isn't already
+ */
+ entry_ptr->is_dirty = TRUE;
+
+ /* update for change in entry size if necessary */
+ if ( entry_ptr->size != new_size ) {
+
+ /* update the protected entry list */
+ H5C2__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr->pel_len), \
+ (cache_ptr->pel_size), \
+ (entry_ptr->size), (new_size));
+
+ /* update the hash table */
+ H5C2__UPDATE_INDEX_FOR_SIZE_CHANGE((cache_ptr), (entry_ptr->size),\
+ (new_size));
+
+ /* if the entry is in the skip list, update that too */
+ if ( entry_ptr->in_slist ) {
+
+ H5C2__UPDATE_SLIST_FOR_SIZE_CHANGE((cache_ptr), (entry_ptr->size),\
+ (new_size));
+ }
+
+ /* update statistics just before changing the entry size */
+ H5C2__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE((cache_ptr), (entry_ptr), \
+ (new_size));
+
+ /* finally, update the entry size proper */
+ entry_ptr->size = new_size;
+ }
+
+ if ( ! (entry_ptr->in_slist) ) {
+
+ H5C2__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
+ }
+
+ H5C2__UPDATE_STATS_FOR_DIRTY_PIN(cache_ptr, entry_ptr)
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_resize_pinned_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_pin_protected_entry()
+ *
+ * Purpose: Pin a protected cache entry. The entry must be protected
+ * at the time of call, and must be unpinned.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 4/26/06
+ *
+ * Modifications:
+ *
+ * JRM -- 4/26/06
+ * Modified routine to allow it to operate on protected
+ * entries.
+ *
+ * JRM -- 2/16/07
+ * Added conditional compile to avoid unused parameter
+ * warning in production compile.
+ *
+ * JRM -- 4/4/07
+ * Fixed typo -- canged macro call to
+ * H5C2__UPDATE_STATS_FOR_UNPIN to call to
+ * H5C2__UPDATE_STATS_FOR_PIN.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NDEBUG
+herr_t
+H5C2_pin_protected_entry(H5C2_t * cache_ptr,
+ void * thing)
+#else
+herr_t
+H5C2_pin_protected_entry(H5C2_t UNUSED * cache_ptr,
+ void * thing)
+#endif
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5C2_cache_entry_t * entry_ptr;
+
+ FUNC_ENTER_NOAPI(H5C2_pin_protected_entry, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( thing );
+
+ entry_ptr = (H5C2_cache_entry_t *)thing;
+
+ HDassert( H5F_addr_defined(entry_ptr->addr) );
+
+ if ( ! ( entry_ptr->is_protected ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, "Entry isn't protected")
+ }
+
+ if ( entry_ptr->is_pinned ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, "Entry is already pinned")
+ }
+
+ entry_ptr->is_pinned = TRUE;
+
+ H5C2__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr)
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_pin_protected_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_protect
+ *
+ * Purpose: If the target entry is not in the cache, load it. If
+ * necessary, attempt to evict one or more entries to keep
+ * the cache within its maximum size.
+ *
+ * Mark the target entry as protected, and return its address
+ * to the caller. The caller must call H5C2_unprotect() when
+ * finished with the entry.
+ *
+ * While it is protected, the entry may not be either evicted
+ * or flushed -- nor may it be accessed by another call to
+ * H5C2_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
+ *
+ * Programmer: John Mainzer - 6/2/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/21/04
+ * Updated for the addition of the hash table.
+ *
+ * JRM -- 10/28/04
+ * Added code to set cache_full to TRUE whenever we try to
+ * make space in the cache.
+ *
+ * JRM -- 11/12/04
+ * Added code to call to H5C2_make_space_in_cache()
+ * after the call to H5C2__auto_adjust_cache_size() if that
+ * function sets the size_decreased flag is TRUE.
+ *
+ * JRM -- 4/25/05
+ * The size_decreased flag can also be set to TRUE in
+ * H5C2_set_cache_auto_resize_config() if a new configuration
+ * forces an immediate reduction in cache size. Modified
+ * the code to deal with this eventuallity.
+ *
+ * JRM -- 6/24/05
+ * Added support for the new write_permitted field of H5C2_t.
+ *
+ * JRM -- 10/22/05
+ * Hand optimizations.
+ *
+ * JRM -- 5/3/06
+ * Added code to set the new dirtied field in
+ * H5C2_cache_entry_t to FALSE prior to return.
+ *
+ * JRM -- 6/23/06
+ * Modified code to allow dirty entries to be loaded from
+ * disk. This is necessary as a bug fix in the object
+ * header code requires us to modify a header as it is read.
+ *
+ * JRM -- 3/28/07
+ * Added the flags parameter and supporting code. At least
+ * for now, this parameter is used to allow the entry to
+ * be protected read only, thus allowing multiple protects.
+ *
+ * Also added code to allow multiple read only protects
+ * of cache entries.
+ *
+ * JRM -- 7/27/07
+ * Added code supporting the new evictions_enabled fieled
+ * in H5C2_t.
+ *
+ * JRM -- 7/11/07
+ * Revised function for the new metadata cache API. The
+ * function lost its point to H5F_t (now supplied via
+ * *cache_ptr), one of its dxpl ids. and one of the udata
+ * fields. Gained the len parameter. Also internal
+ * changes supporting the revised API.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+void *
+H5C2_protect(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ const H5C2_class_t * type,
+ haddr_t addr,
+ size_t len,
+ const void * udata,
+ unsigned flags)
+{
+ /* const char * fcn_name = "H5C2_protect()"; */
+ hbool_t hit;
+ hbool_t first_flush;
+ hbool_t have_write_permitted = FALSE;
+ hbool_t read_only = FALSE;
+ hbool_t write_permitted;
+ hbool_t chk_len = FALSE;
+ herr_t result;
+ void * thing;
+ H5C2_cache_entry_t * entry_ptr;
+ void * ret_value; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_protect, NULL)
+
+ /* check args */
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( cache_ptr->skip_file_checks || cache_ptr->f );
+ HDassert( type );
+ HDassert( H5F_addr_defined(addr) );
+ HDassert( len > 0 );
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HDassert(0);
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ if ( (flags & H5C2__READ_ONLY_FLAG) != 0 )
+ {
+ read_only = TRUE;
+ }
+
+ if ( (flags & H5C2__CHECK_SIZE_FLAG) != 0 )
+ {
+ chk_len = TRUE;
+ }
+
+ /* first check to see if the target is in cache */
+ H5C2__SEARCH_INDEX(cache_ptr, addr, entry_ptr, NULL)
+
+ if ( entry_ptr != NULL ) {
+
+ hit = TRUE;
+ thing = (void *)entry_ptr;
+
+ } else {
+
+ /* must try to load the entry from disk. */
+
+ hit = FALSE;
+
+ thing = H5C2_load_entry(cache_ptr->f, dxpl_id, type,
+ addr, len, chk_len, udata);
+
+ if ( thing == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't load entry")
+ }
+
+ entry_ptr = (H5C2_cache_entry_t *)thing;
+
+ /* try to free up some space if necessary and if
+ * evictions are permitted
+ */
+ if ( ( cache_ptr->evictions_enabled ) &&
+ ( (cache_ptr->index_size + entry_ptr->size) >
+ cache_ptr->max_cache_size ) ) {
+
+ size_t space_needed;
+
+ cache_ptr->cache_full = TRUE;
+
+ if ( cache_ptr->check_write_permitted != NULL ) {
+
+ result = (cache_ptr->check_write_permitted)(cache_ptr->f,
+ dxpl_id,
+ &write_permitted);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \
+ "Can't get write_permitted 1")
+
+ } else {
+
+ have_write_permitted = TRUE;
+
+ first_flush = TRUE;
+ }
+ } else {
+
+ write_permitted = cache_ptr->write_permitted;
+
+ have_write_permitted = TRUE;
+
+ first_flush = TRUE;
+ }
+
+ HDassert( entry_ptr->size <= H5C2_MAX_ENTRY_SIZE );
+
+ space_needed = entry_ptr->size;
+
+ if ( space_needed > cache_ptr->max_cache_size ) {
+
+ space_needed = cache_ptr->max_cache_size;
+ }
+
+ /* Note that space_needed is just the amount of space that
+ * needed to insert the new entry without exceeding the cache
+ * size limit. The subsequent call to H5C2_make_space_in_cache()
+ * may evict the entries required to free more or less space
+ * depending on conditions. It MAY be less if the cache is
+ * currently undersized, or more if the cache is oversized.
+ *
+ * The cache can exceed its maximum size limit via the following
+ * mechanisms:
+ *
+ * First, it is possible for the cache to grow without
+ * bound as long as entries are protected and not unprotected.
+ *
+ * Second, when writes are not permitted it is also possible
+ * for the cache to grow without bound.
+ *
+ * Third, the user may choose to disable evictions -- causing
+ * the cache to grow without bound until evictions are
+ * re-enabled.
+ *
+ * Finally, we usually don't check to see if the cache is
+ * oversized at the end of an unprotect. As a result, it is
+ * possible to have a vastly oversized cache with no protected
+ * entries as long as all the protects preceed the unprotects.
+ *
+ * Since items 1, 2, and 3 are not changing any time soon, I
+ * see no point in worrying about the fourth.
+ */
+
+ result = H5C2_make_space_in_cache(dxpl_id, cache_ptr,
+ space_needed, write_permitted);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \
+ "H5C2_make_space_in_cache failed 1.")
+ }
+ }
+
+ /* Insert the entry in the hash table. It can't be dirty yet, so
+ * we don't even check to see if it should go in the skip list.
+ *
+ * This is no longer true -- due to a bug fix, we may modify
+ * data on load to repair a file.
+ */
+ H5C2__INSERT_IN_INDEX(cache_ptr, entry_ptr, NULL)
+
+ if ( ( entry_ptr->is_dirty ) && ( ! (entry_ptr->in_slist) ) ) {
+
+ H5C2__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, NULL)
+ }
+
+ /* insert the entry in the data structures used by the replacement
+ * policy. We are just going to take it out again when we update
+ * the replacement policy for a protect, but this simplifies the
+ * code. If we do this often enough, we may want to optimize this.
+ */
+ H5C2__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, NULL)
+ }
+
+ HDassert( entry_ptr->addr == addr );
+ HDassert( entry_ptr->type == type );
+
+ if ( entry_ptr->is_protected ) {
+
+ if ( ( read_only ) && ( entry_ptr->is_read_only ) ) {
+
+ HDassert( entry_ptr->ro_ref_count > 0 );
+
+ (entry_ptr->ro_ref_count)++;
+
+ } else {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \
+ "Target already protected & not read only?!?.")
+ }
+ } else {
+
+ H5C2__UPDATE_RP_FOR_PROTECT(cache_ptr, entry_ptr, NULL)
+
+ entry_ptr->is_protected = TRUE;
+
+ if ( read_only ) {
+
+ entry_ptr->is_read_only = TRUE;
+ entry_ptr->ro_ref_count = 1;
+ }
+
+ entry_ptr->dirtied = FALSE;
+ }
+
+ H5C2__UPDATE_CACHE_HIT_RATE_STATS(cache_ptr, hit)
+
+ H5C2__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit)
+
+ ret_value = thing;
+
+ if ( ( cache_ptr->evictions_enabled ) &&
+ ( ( cache_ptr->size_decreased ) ||
+ ( ( cache_ptr->resize_enabled ) &&
+ ( cache_ptr->cache_accesses >=
+ (cache_ptr->resize_ctl).epoch_length ) ) ) ) {
+
+ if ( ! have_write_permitted ) {
+
+ if ( cache_ptr->check_write_permitted != NULL ) {
+
+ result = (cache_ptr->check_write_permitted)(cache_ptr->f,
+ dxpl_id,
+ &write_permitted);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \
+ "Can't get write_permitted 2")
+
+ } else {
+
+ have_write_permitted = TRUE;
+
+ first_flush = TRUE;
+ }
+ } else {
+
+ write_permitted = cache_ptr->write_permitted;
+
+ have_write_permitted = TRUE;
+
+ first_flush = TRUE;
+ }
+ }
+
+ if ( ( cache_ptr->resize_enabled ) &&
+ ( cache_ptr->cache_accesses >=
+ (cache_ptr->resize_ctl).epoch_length ) ) {
+
+ result = H5C2__auto_adjust_cache_size(cache_ptr,
+ dxpl_id,
+ write_permitted);
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \
+ "Cache auto-resize failed.")
+ }
+ }
+
+ if ( cache_ptr->size_decreased ) {
+
+ cache_ptr->size_decreased = FALSE;
+
+ /* check to see if the cache is now oversized due to the cache
+ * size reduction. If it is, try to evict enough entries to
+ * bring the cache size down to the current maximum cache size.
+ */
+ if ( cache_ptr->index_size > cache_ptr->max_cache_size ) {
+
+ cache_ptr->cache_full = TRUE;
+
+ result = H5C2_make_space_in_cache(dxpl_id,
+ cache_ptr,
+ (size_t)0,
+ write_permitted);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \
+ "H5C2_make_space_in_cache failed 2.")
+ }
+ }
+ }
+ }
+
+done:
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HDassert(0);
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_protect() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_reset_cache_hit_rate_stats()
+ *
+ * Purpose: Reset the cache hit rate computation fields.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer, 10/5/04
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_reset_cache_hit_rate_stats(H5C2_t * cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_reset_cache_hit_rate_stats, FAIL)
+
+ if ( ( cache_ptr == NULL ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry.")
+ }
+
+ cache_ptr->cache_hits = 0;
+ cache_ptr->cache_accesses = 0;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_reset_cache_hit_rate_stats() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_set_cache_auto_resize_config
+ *
+ * Purpose: Set the cache automatic resize configuration to the
+ * provided values if they are in range, and fail if they
+ * are not.
+ *
+ * If the new configuration enables automatic cache resizing,
+ * coerce the cache max size and min clean size into agreement
+ * with the new policy and re-set the full cache hit rate
+ * stats.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 10/8/04
+ *
+ * Modifications:
+ *
+ * JRM -- 11/18/04
+ * Reworked function to match major changes in
+ * H5C2_auto_size_ctl_t.
+ *
+ * JRM -- 4/25/05
+ * Added code to set cache_ptr->size_decreased to TRUE
+ * if the new configuration forces an immediate reduction
+ * in cache size.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_set_cache_auto_resize_config(H5C2_t * cache_ptr,
+ H5C2_auto_size_ctl_t *config_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ herr_t result;
+ size_t new_max_cache_size;
+ size_t new_min_clean_size;
+
+ FUNC_ENTER_NOAPI(H5C2_set_cache_auto_resize_config, FAIL)
+
+ if ( ( cache_ptr == NULL ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry.")
+ }
+
+ if ( config_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "NULL config_ptr on entry.")
+ }
+
+ if ( config_ptr->version != H5C2__CURR_AUTO_SIZE_CTL_VER ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown config version.")
+ }
+
+ /* check general configuration section of the config: */
+ if ( SUCCEED != H5C2_validate_resize_config(config_ptr,
+ H5C2_RESIZE_CFG__VALIDATE_GENERAL) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, \
+ "error in general configuration fields of new config.")
+ }
+
+ /* check size increase control fields of the config: */
+ if ( SUCCEED != H5C2_validate_resize_config(config_ptr,
+ H5C2_RESIZE_CFG__VALIDATE_INCREMENT) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, \
+ "error in the size increase control fields of new config.")
+ }
+
+ /* check size decrease control fields of the config: */
+ if ( SUCCEED != H5C2_validate_resize_config(config_ptr,
+ H5C2_RESIZE_CFG__VALIDATE_DECREMENT) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, \
+ "error in the size decrease control fields of new config.")
+ }
+
+ /* check for conflicts between size increase and size decrease controls: */
+ if ( SUCCEED != H5C2_validate_resize_config(config_ptr,
+ H5C2_RESIZE_CFG__VALIDATE_INTERACTIONS) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, \
+ "conflicting threshold fields in new config.")
+ }
+
+ cache_ptr->size_increase_possible = TRUE; /* will set to FALSE if needed */
+ cache_ptr->size_decrease_possible = TRUE; /* will set to FALSE if needed */
+
+ switch ( config_ptr->incr_mode )
+ {
+ case H5C2_incr__off:
+ cache_ptr->size_increase_possible = FALSE;
+ break;
+
+ case H5C2_incr__threshold:
+ if ( ( config_ptr->lower_hr_threshold <= 0.0 ) ||
+ ( config_ptr->increment <= 1.0 ) ||
+ ( ( config_ptr->apply_max_increment ) &&
+ ( config_ptr->max_increment <= 0 ) ) ) {
+
+ cache_ptr->size_increase_possible = FALSE;
+ }
+ break;
+
+ default: /* should be unreachable */
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown incr_mode?!?!?.")
+ }
+
+ switch ( config_ptr->decr_mode )
+ {
+ case H5C2_decr__off:
+ cache_ptr->size_decrease_possible = FALSE;
+ break;
+
+ case H5C2_decr__threshold:
+ if ( ( config_ptr->upper_hr_threshold >= 1.0 ) ||
+ ( config_ptr->decrement >= 1.0 ) ||
+ ( ( config_ptr->apply_max_decrement ) &&
+ ( config_ptr->max_decrement <= 0 ) ) ) {
+
+ cache_ptr->size_decrease_possible = FALSE;
+ }
+ break;
+
+ case H5C2_decr__age_out:
+ if ( ( ( config_ptr->apply_empty_reserve ) &&
+ ( config_ptr->empty_reserve >= 1.0 ) ) ||
+ ( ( config_ptr->apply_max_decrement ) &&
+ ( config_ptr->max_decrement <= 0 ) ) ) {
+
+ cache_ptr->size_decrease_possible = FALSE;
+ }
+ break;
+
+ case H5C2_decr__age_out_with_threshold:
+ if ( ( ( config_ptr->apply_empty_reserve ) &&
+ ( config_ptr->empty_reserve >= 1.0 ) ) ||
+ ( ( config_ptr->apply_max_decrement ) &&
+ ( config_ptr->max_decrement <= 0 ) ) ||
+ ( config_ptr->upper_hr_threshold >= 1.0 ) ) {
+
+ cache_ptr->size_decrease_possible = FALSE;
+ }
+ break;
+
+ default: /* should be unreachable */
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown decr_mode?!?!?.")
+ }
+
+ if ( config_ptr->max_size == config_ptr->min_size ) {
+
+ cache_ptr->size_increase_possible = FALSE;
+ cache_ptr->size_decrease_possible = FALSE;
+ }
+
+ cache_ptr->resize_enabled = cache_ptr->size_increase_possible ||
+ cache_ptr->size_decrease_possible;
+
+ cache_ptr->resize_ctl = *config_ptr;
+
+ /* Resize the cache to the supplied initial value if requested, or as
+ * necessary to force it within the bounds of the current automatic
+ * cache resizing configuration.
+ *
+ * Note that the min_clean_fraction may have changed, so we
+ * go through the exercise even if the current size is within
+ * range and an initial size has not been provided.
+ */
+ if ( (cache_ptr->resize_ctl).set_initial_size ) {
+
+ new_max_cache_size = (cache_ptr->resize_ctl).initial_size;
+ }
+ else if ( cache_ptr->max_cache_size > (cache_ptr->resize_ctl).max_size ) {
+
+ new_max_cache_size = (cache_ptr->resize_ctl).max_size;
+ }
+ else if ( cache_ptr->max_cache_size < (cache_ptr->resize_ctl).min_size ) {
+
+ new_max_cache_size = (cache_ptr->resize_ctl).min_size;
+
+ } else {
+
+ new_max_cache_size = cache_ptr->max_cache_size;
+ }
+
+ new_min_clean_size = (size_t)
+ ((double)new_max_cache_size *
+ ((cache_ptr->resize_ctl).min_clean_fraction));
+
+
+ /* since new_min_clean_size is of type size_t, we have
+ *
+ * ( 0 <= new_min_clean_size )
+ *
+ * by definition.
+ */
+ HDassert( new_min_clean_size <= new_max_cache_size );
+ HDassert( (cache_ptr->resize_ctl).min_size <= new_max_cache_size );
+ HDassert( new_max_cache_size <= (cache_ptr->resize_ctl).max_size );
+
+ if ( new_max_cache_size < cache_ptr->max_cache_size ) {
+
+ cache_ptr->size_decreased = TRUE;
+ }
+
+ cache_ptr->max_cache_size = new_max_cache_size;
+ cache_ptr->min_clean_size = new_min_clean_size;
+
+ if ( H5C2_reset_cache_hit_rate_stats(cache_ptr) != SUCCEED ) {
+
+ /* this should be impossible... */
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_reset_cache_hit_rate_stats failed.")
+ }
+
+ /* remove excess epoch markers if any */
+ if ( ( config_ptr->decr_mode == H5C2_decr__age_out_with_threshold ) ||
+ ( config_ptr->decr_mode == H5C2_decr__age_out ) ) {
+
+ if ( cache_ptr->epoch_markers_active >
+ (cache_ptr->resize_ctl).epochs_before_eviction ) {
+
+ result =
+ H5C2__autoadjust__ageout__remove_excess_markers(cache_ptr);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "can't remove excess epoch markers.")
+ }
+ }
+ } else if ( cache_ptr->epoch_markers_active > 0 ) {
+
+ result = H5C2__autoadjust__ageout__remove_all_markers(cache_ptr);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "error removing all epoch markers.")
+ }
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_set_cache_auto_resize_config() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_set_evictions_enabled()
+ *
+ * Purpose: Set cache_ptr->evictions_enabled to the value of the
+ * evictions enabled parameter.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 7/27/07
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_set_evictions_enabled(H5C2_t * cache_ptr,
+ hbool_t evictions_enabled)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_set_evictions_enabled, FAIL)
+
+ if ( ( cache_ptr == NULL ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry.")
+ }
+
+ if ( ( evictions_enabled != TRUE ) && ( evictions_enabled != FALSE ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Bad evictions_enabled on entry.")
+ }
+
+ /* There is no fundamental reason why we should not permit
+ * evictions to be disabled while automatic resize is enabled.
+ * However, I can't think of any good reason why one would
+ * want to, and allowing it would greatly complicate testing
+ * the feature. Hence the following:
+ */
+ if ( ( evictions_enabled != TRUE ) &&
+ ( ( cache_ptr->resize_ctl.incr_mode != H5C2_incr__off ) ||
+ ( cache_ptr->resize_ctl.decr_mode != H5C2_decr__off ) ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Can't disable evictions when auto resize enabled.")
+ }
+
+ cache_ptr->evictions_enabled = evictions_enabled;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_set_evictions_enabled() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_set_prefix
+ *
+ * Purpose: Set the values of the prefix field of H5C2_t. This
+ * filed is used to label some debugging output.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 1/20/06
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_set_prefix(H5C2_t * cache_ptr,
+ char * prefix)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_set_prefix, FAIL)
+
+ /* This would normally be an assert, but we need to use an HGOTO_ERROR
+ * call to shut up the compiler.
+ */
+ if ( ( ! cache_ptr ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr")
+ }
+
+ HDassert( prefix );
+ HDassert( HDstrlen(prefix) < H5C2__PREFIX_LEN ) ;
+
+ HDstrcpy(&(cache_ptr->prefix[0]), prefix);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_set_prefix() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_set_skip_flags
+ *
+ * Purpose: Set the values of the skip sanity check flags.
+ *
+ * This function and the skip sanity check flags were created
+ * for the convenience of the test bed. However it is
+ * possible that there may be other uses for the flags.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/11/04
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_set_skip_flags(H5C2_t * cache_ptr,
+ hbool_t skip_file_checks,
+ hbool_t skip_dxpl_id_checks)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_set_skip_flags, FAIL)
+
+ /* This would normally be an assert, but we need to use an HGOTO_ERROR
+ * call to shut up the compiler.
+ */
+ if ( ( ! cache_ptr ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr")
+ }
+
+ cache_ptr->skip_file_checks = skip_file_checks;
+ cache_ptr->skip_dxpl_id_checks = skip_dxpl_id_checks;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_set_skip_flags() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_set_trace_file_ptr
+ *
+ * Purpose: Set the trace_file_ptr field for the cache.
+ *
+ * This field must either be NULL (which turns of trace
+ * file logging), or be a pointer to an open file to which
+ * trace file data is to be written.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 1/20/06
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_set_trace_file_ptr(H5C2_t * cache_ptr,
+ FILE * trace_file_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_set_trace_file_ptr, FAIL)
+
+ /* This would normally be an assert, but we need to use an HGOTO_ERROR
+ * call to shut up the compiler.
+ */
+ if ( ( ! cache_ptr ) || ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr")
+ }
+
+ cache_ptr->trace_file_ptr = trace_file_ptr;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_set_trace_file_ptr() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_stats
+ *
+ * Purpose: Prints statistics about the cache.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/2/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/21/04
+ * Updated function for the addition of the hash table.
+ *
+ * JRM -- 9/8/05
+ * Updated function for the addition of cache entry size
+ * change statistics.
+ *
+ * JRM -- 1/13/06
+ * Added code to use the prefix field of H5C2_t to allow
+ * tagging of statistics output.
+ *
+ * JRM -- 3/21/06
+ * Added code supporting the pinned entry related stats.
+ *
+ * JRM -- 8/9/06
+ * More code supporting pinned entry related stats.
+ *
+ * JRM -- 8/23/06
+ * Added code supporting new flush related statistics.
+ *
+ * JRM -- 3/31/07
+ * Added code supporting the new write_protects,
+ * read_protects, and max_read_protects fields.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_stats(H5C2_t * cache_ptr,
+ const char * cache_name,
+ hbool_t
+#if !H5C2_COLLECT_CACHE_STATS
+ UNUSED
+#endif /* H5C2_COLLECT_CACHE_STATS */
+ display_detailed_stats)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+#if H5C2_COLLECT_CACHE_STATS
+ int i;
+ int64_t total_hits = 0;
+ int64_t total_misses = 0;
+ int64_t total_write_protects = 0;
+ int64_t total_read_protects = 0;
+ int64_t max_read_protects = 0;
+ int64_t total_insertions = 0;
+ int64_t total_pinned_insertions = 0;
+ int64_t total_clears = 0;
+ int64_t total_flushes = 0;
+ int64_t total_evictions = 0;
+ int64_t total_renames = 0;
+ int64_t total_entry_flush_renames = 0;
+ int64_t total_cache_flush_renames = 0;
+ int64_t total_size_increases = 0;
+ int64_t total_size_decreases = 0;
+ int64_t total_entry_flush_size_changes = 0;
+ int64_t total_cache_flush_size_changes = 0;
+ int64_t total_pins = 0;
+ int64_t total_unpins = 0;
+ int64_t total_dirty_pins = 0;
+ int64_t total_pinned_flushes = 0;
+ int64_t total_pinned_clears = 0;
+ int32_t aggregate_max_accesses = 0;
+ int32_t aggregate_min_accesses = 1000000;
+ int32_t aggregate_max_clears = 0;
+ int32_t aggregate_max_flushes = 0;
+ size_t aggregate_max_size = 0;
+ int32_t aggregate_max_pins = 0;
+ double hit_rate;
+ double average_successful_search_depth = 0.0;
+ double average_failed_search_depth = 0.0;
+#endif /* H5C2_COLLECT_CACHE_STATS */
+
+ FUNC_ENTER_NOAPI(H5C2_stats, FAIL)
+
+ /* This would normally be an assert, but we need to use an HGOTO_ERROR
+ * call to shut up the compiler.
+ */
+ if ( ( ! cache_ptr ) ||
+ ( cache_ptr->magic != H5C2__H5C2_T_MAGIC ) ||
+ ( !cache_name ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr or cache_name")
+ }
+
+#if H5C2_COLLECT_CACHE_STATS
+
+ for ( i = 0; i <= cache_ptr->max_type_id; i++ ) {
+
+ total_hits += cache_ptr->hits[i];
+ total_misses += cache_ptr->misses[i];
+ total_write_protects += cache_ptr->write_protects[i];
+ total_read_protects += cache_ptr->read_protects[i];
+ if ( max_read_protects < cache_ptr->max_read_protects[i] ) {
+ max_read_protects = cache_ptr->max_read_protects[i];
+ }
+ total_insertions += cache_ptr->insertions[i];
+ total_pinned_insertions += cache_ptr->pinned_insertions[i];
+ total_clears += cache_ptr->clears[i];
+ total_flushes += cache_ptr->flushes[i];
+ total_evictions += cache_ptr->evictions[i];
+ total_renames += cache_ptr->renames[i];
+ total_entry_flush_renames
+ += cache_ptr->entry_flush_renames[i];
+ total_cache_flush_renames
+ += cache_ptr->cache_flush_renames[i];
+ total_size_increases += cache_ptr->size_increases[i];
+ total_size_decreases += cache_ptr->size_decreases[i];
+ total_entry_flush_size_changes
+ += cache_ptr->entry_flush_size_changes[i];
+ total_cache_flush_size_changes
+ += cache_ptr->cache_flush_size_changes[i];
+ total_pins += cache_ptr->pins[i];
+ total_unpins += cache_ptr->unpins[i];
+ total_dirty_pins += cache_ptr->dirty_pins[i];
+ total_pinned_flushes += cache_ptr->pinned_flushes[i];
+ total_pinned_clears += cache_ptr->pinned_clears[i];
+#if H5C2_COLLECT_CACHE_ENTRY_STATS
+ if ( aggregate_max_accesses < cache_ptr->max_accesses[i] )
+ aggregate_max_accesses = cache_ptr->max_accesses[i];
+ if ( aggregate_min_accesses > aggregate_max_accesses )
+ aggregate_min_accesses = aggregate_max_accesses;
+ if ( aggregate_min_accesses > cache_ptr->min_accesses[i] )
+ aggregate_min_accesses = cache_ptr->min_accesses[i];
+ if ( aggregate_max_clears < cache_ptr->max_clears[i] )
+ aggregate_max_clears = cache_ptr->max_clears[i];
+ if ( aggregate_max_flushes < cache_ptr->max_flushes[i] )
+ aggregate_max_flushes = cache_ptr->max_flushes[i];
+ if ( aggregate_max_size < cache_ptr->max_size[i] )
+ aggregate_max_size = cache_ptr->max_size[i];
+ if ( aggregate_max_pins < cache_ptr->max_pins[i] )
+ aggregate_max_pins = cache_ptr->max_pins[i];
+#endif /* H5C2_COLLECT_CACHE_ENTRY_STATS */
+ }
+
+ if ( ( total_hits > 0 ) || ( total_misses > 0 ) ) {
+
+ hit_rate = 100.0 * ((double)(total_hits)) /
+ ((double)(total_hits + total_misses));
+ } else {
+ hit_rate = 0.0;
+ }
+
+ if ( cache_ptr->successful_ht_searches > 0 ) {
+
+ average_successful_search_depth =
+ ((double)(cache_ptr->total_successful_ht_search_depth)) /
+ ((double)(cache_ptr->successful_ht_searches));
+ }
+
+ if ( cache_ptr->failed_ht_searches > 0 ) {
+
+ average_failed_search_depth =
+ ((double)(cache_ptr->total_failed_ht_search_depth)) /
+ ((double)(cache_ptr->failed_ht_searches));
+ }
+
+
+ HDfprintf(stdout, "\n%sH5C2: cache statistics for %s\n",
+ cache_ptr->prefix, cache_name);
+
+ HDfprintf(stdout, "\n");
+
+ HDfprintf(stdout,
+ "%s hash table insertion / deletions = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->total_ht_insertions),
+ (long)(cache_ptr->total_ht_deletions));
+
+ HDfprintf(stdout,
+ "%s HT successful / failed searches = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->successful_ht_searches),
+ (long)(cache_ptr->failed_ht_searches));
+
+ HDfprintf(stdout,
+ "%s Av. HT suc / failed search depth = %f / %f\n",
+ cache_ptr->prefix,
+ average_successful_search_depth,
+ average_failed_search_depth);
+
+ HDfprintf(stdout,
+ "%s current (max) index size / length = %ld (%ld) / %ld (%ld)\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->index_size),
+ (long)(cache_ptr->max_index_size),
+ (long)(cache_ptr->index_len),
+ (long)(cache_ptr->max_index_len));
+
+ HDfprintf(stdout,
+ "%s current (max) slist size / length = %ld (%ld) / %ld (%ld)\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->slist_size),
+ (long)(cache_ptr->max_slist_size),
+ (long)(cache_ptr->slist_len),
+ (long)(cache_ptr->max_slist_len));
+
+ HDfprintf(stdout,
+ "%s current (max) PL size / length = %ld (%ld) / %ld (%ld)\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->pl_size),
+ (long)(cache_ptr->max_pl_size),
+ (long)(cache_ptr->pl_len),
+ (long)(cache_ptr->max_pl_len));
+
+ HDfprintf(stdout,
+ "%s current (max) PEL size / length = %ld (%ld) / %ld (%ld)\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->pel_size),
+ (long)(cache_ptr->max_pel_size),
+ (long)(cache_ptr->pel_len),
+ (long)(cache_ptr->max_pel_len));
+
+ HDfprintf(stdout,
+ "%s current LRU list size / length = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->LRU_list_size),
+ (long)(cache_ptr->LRU_list_len));
+
+ HDfprintf(stdout,
+ "%s current clean LRU size / length = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->cLRU_list_size),
+ (long)(cache_ptr->cLRU_list_len));
+
+ HDfprintf(stdout,
+ "%s current dirty LRU size / length = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->dLRU_list_size),
+ (long)(cache_ptr->dLRU_list_len));
+
+ HDfprintf(stdout,
+ "%s Total hits / misses / hit_rate = %ld / %ld / %f\n",
+ cache_ptr->prefix,
+ (long)total_hits,
+ (long)total_misses,
+ hit_rate);
+
+ HDfprintf(stdout,
+ "%s Total write / read (max) protects = %ld / %ld (%d)\n",
+ cache_ptr->prefix,
+ (long)total_write_protects,
+ (long)total_read_protects,
+ max_read_protects);
+
+ HDfprintf(stdout,
+ "%s Total clears / flushes / evictions = %ld / %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)total_clears,
+ (long)total_flushes,
+ (long)total_evictions);
+
+ HDfprintf(stdout,
+ "%s Total insertions(pinned) / renames = %ld(%ld) / %ld\n",
+ cache_ptr->prefix,
+ (long)total_insertions,
+ (long)total_pinned_insertions,
+ (long)total_renames);
+
+ HDfprintf(stdout,
+ "%s Total entry / cache flush renames = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)total_entry_flush_renames,
+ (long)total_cache_flush_renames);
+
+ HDfprintf(stdout, "%s Total entry size incrs / decrs = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)total_size_increases,
+ (long)total_size_decreases);
+
+ HDfprintf(stdout, "%s Ttl entry/cache flush size changes = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)total_entry_flush_size_changes,
+ (long)total_cache_flush_size_changes);
+
+ HDfprintf(stdout,
+ "%s Total entry pins (dirty) / unpins = %ld (%ld) / %ld\n",
+ cache_ptr->prefix,
+ (long)total_pins,
+ (long)total_dirty_pins,
+ (long)total_unpins);
+
+ HDfprintf(stdout, "%s Total pinned flushes / clears = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)total_pinned_flushes,
+ (long)total_pinned_clears);
+
+#if H5C2_COLLECT_CACHE_ENTRY_STATS
+
+ HDfprintf(stdout, "%s aggregate max / min accesses = %d / %d\n",
+ cache_ptr->prefix,
+ (int)aggregate_max_accesses,
+ (int)aggregate_min_accesses);
+
+ HDfprintf(stdout, "%s aggregate max_clears / max_flushes = %d / %d\n",
+ cache_ptr->prefix,
+ (int)aggregate_max_clears,
+ (int)aggregate_max_flushes);
+
+ HDfprintf(stdout, "%s aggregate max_size / max_pins = %d / %d\n",
+ cache_ptr->prefix,
+ (int)aggregate_max_size,
+ (int)aggregate_max_pins);
+
+#endif /* H5C2_COLLECT_CACHE_ENTRY_STATS */
+
+ if ( display_detailed_stats )
+ {
+
+ for ( i = 0; i <= cache_ptr->max_type_id; i++ ) {
+
+ HDfprintf(stdout, "\n");
+
+ HDfprintf(stdout, "%s Stats on %s:\n",
+ cache_ptr->prefix,
+ ((cache_ptr->type_name_table_ptr))[i]);
+
+ if ( ( cache_ptr->hits[i] > 0 ) || ( cache_ptr->misses[i] > 0 ) ) {
+
+ hit_rate = 100.0 * ((double)(cache_ptr->hits[i])) /
+ ((double)(cache_ptr->hits[i] + cache_ptr->misses[i]));
+ } else {
+ hit_rate = 0.0;
+ }
+
+ HDfprintf(stdout,
+ "%s hits / misses / hit_rate = %ld / %ld / %f\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->hits[i]),
+ (long)(cache_ptr->misses[i]),
+ hit_rate);
+
+ HDfprintf(stdout,
+ "%s write / read (max) protects = %ld / %ld (%d)\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->write_protects[i]),
+ (long)(cache_ptr->read_protects[i]),
+ (int)(cache_ptr->max_read_protects[i]));
+
+ HDfprintf(stdout,
+ "%s clears / flushes / evictions = %ld / %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->clears[i]),
+ (long)(cache_ptr->flushes[i]),
+ (long)(cache_ptr->evictions[i]));
+
+ HDfprintf(stdout,
+ "%s insertions(pinned) / renames = %ld(%ld) / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->insertions[i]),
+ (long)(cache_ptr->pinned_insertions[i]),
+ (long)(cache_ptr->renames[i]));
+
+ HDfprintf(stdout,
+ "%s entry / cache flush renames = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->entry_flush_renames[i]),
+ (long)(cache_ptr->cache_flush_renames[i]));
+
+ HDfprintf(stdout,
+ "%s size increases / decreases = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->size_increases[i]),
+ (long)(cache_ptr->size_decreases[i]));
+
+ HDfprintf(stdout,
+ "%s entry/cache flush size changes = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->entry_flush_size_changes[i]),
+ (long)(cache_ptr->cache_flush_size_changes[i]));
+
+
+ HDfprintf(stdout,
+ "%s entry pins / unpins = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->pins[i]),
+ (long)(cache_ptr->unpins[i]));
+
+ HDfprintf(stdout,
+ "%s entry dirty pins/pin'd flushes = %ld / %ld\n",
+ cache_ptr->prefix,
+ (long)(cache_ptr->dirty_pins[i]),
+ (long)(cache_ptr->pinned_flushes[i]));
+
+#if H5C2_COLLECT_CACHE_ENTRY_STATS
+
+ HDfprintf(stdout,
+ "%s entry max / min accesses = %d / %d\n",
+ cache_ptr->prefix,
+ cache_ptr->max_accesses[i],
+ cache_ptr->min_accesses[i]);
+
+ HDfprintf(stdout,
+ "%s entry max_clears / max_flushes = %d / %d\n",
+ cache_ptr->prefix,
+ cache_ptr->max_clears[i],
+ cache_ptr->max_flushes[i]);
+
+ HDfprintf(stdout,
+ "%s entry max_size / max_pins = %d / %d\n",
+ cache_ptr->prefix,
+ (int)(cache_ptr->max_size[i]),
+ (int)(cache_ptr->max_pins[i]));
+
+
+#endif /* H5C2_COLLECT_CACHE_ENTRY_STATS */
+
+ }
+ }
+
+ HDfprintf(stdout, "\n");
+
+#endif /* H5C2_COLLECT_CACHE_STATS */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_stats() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_stats__reset
+ *
+ * Purpose: Reset the stats fields to their initial values.
+ *
+ * Return: void
+ *
+ * Programmer: John Mainzer, 4/28/04
+ *
+ * Modifications:
+ *
+ * JRM - 7/21/04
+ * Updated for hash table related statistics.
+ *
+ * JRM - 9/8/05
+ * Updated for size increase / decrease statistics.
+ *
+ * JRM - 3/20/06
+ * Updated for pin / unpin related statistics.
+ *
+ * JRM - 8/9/06
+ * Further updates for pin related statistics.
+ *
+ * JRM 8/23/06
+ * Added initialization code for new flush related statistics.
+ *
+ * JRM 2/16/07
+ * Added conditional compile code to avoid unused parameter
+ * warning in the production build.
+ *
+ * JRM 3/31/07
+ * Added initialization for the new write_protects,
+ * read_protects, and max_read_protects fields.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+void
+#ifndef NDEBUG
+H5C2_stats__reset(H5C2_t * cache_ptr)
+#else /* NDEBUG */
+#if H5C2_COLLECT_CACHE_STATS
+H5C2_stats__reset(H5C2_t * cache_ptr)
+#else /* H5C2_COLLECT_CACHE_STATS */
+H5C2_stats__reset(H5C2_t UNUSED * cache_ptr)
+#endif /* H5C2_COLLECT_CACHE_STATS */
+#endif /* NDEBUG */
+{
+#if H5C2_COLLECT_CACHE_STATS
+ int i;
+#endif /* H5C2_COLLECT_CACHE_STATS */
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+#if H5C2_COLLECT_CACHE_STATS
+ for ( i = 0; i <= cache_ptr->max_type_id; i++ )
+ {
+ cache_ptr->hits[i] = 0;
+ cache_ptr->misses[i] = 0;
+ cache_ptr->write_protects[i] = 0;
+ cache_ptr->read_protects[i] = 0;
+ cache_ptr->max_read_protects[i] = 0;
+ cache_ptr->insertions[i] = 0;
+ cache_ptr->pinned_insertions[i] = 0;
+ cache_ptr->clears[i] = 0;
+ cache_ptr->flushes[i] = 0;
+ cache_ptr->evictions[i] = 0;
+ cache_ptr->renames[i] = 0;
+ cache_ptr->entry_flush_renames[i] = 0;
+ cache_ptr->cache_flush_renames[i] = 0;
+ cache_ptr->pins[i] = 0;
+ cache_ptr->unpins[i] = 0;
+ cache_ptr->dirty_pins[i] = 0;
+ cache_ptr->pinned_flushes[i] = 0;
+ cache_ptr->pinned_clears[i] = 0;
+ cache_ptr->size_increases[i] = 0;
+ cache_ptr->size_decreases[i] = 0;
+ cache_ptr->entry_flush_size_changes[i] = 0;
+ cache_ptr->cache_flush_size_changes[i] = 0;
+ }
+
+ cache_ptr->total_ht_insertions = 0;
+ cache_ptr->total_ht_deletions = 0;
+ cache_ptr->successful_ht_searches = 0;
+ cache_ptr->total_successful_ht_search_depth = 0;
+ cache_ptr->failed_ht_searches = 0;
+ cache_ptr->total_failed_ht_search_depth = 0;
+
+ cache_ptr->max_index_len = 0;
+ cache_ptr->max_index_size = (size_t)0;
+
+ cache_ptr->max_slist_len = 0;
+ cache_ptr->max_slist_size = (size_t)0;
+
+ cache_ptr->max_pl_len = 0;
+ cache_ptr->max_pl_size = (size_t)0;
+
+ cache_ptr->max_pel_len = 0;
+ cache_ptr->max_pel_size = (size_t)0;
+
+#if H5C2_COLLECT_CACHE_ENTRY_STATS
+
+ for ( i = 0; i <= cache_ptr->max_type_id; i++ )
+ {
+ cache_ptr->max_accesses[i] = 0;
+ cache_ptr->min_accesses[i] = 1000000;
+ cache_ptr->max_clears[i] = 0;
+ cache_ptr->max_flushes[i] = 0;
+ cache_ptr->max_size[i] = (size_t)0;
+ cache_ptr->max_pins[i] = 0;
+ }
+
+#endif /* H5C2_COLLECT_CACHE_ENTRY_STATS */
+#endif /* H5C2_COLLECT_CACHE_STATS */
+
+ return;
+
+} /* H5C2_stats__reset() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_unpin_entry()
+ *
+ * Purpose: Unpin a cache entry. The entry must be unprotected at
+ * the time of call, and must be pinned.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 3/22/06
+ *
+ * Modifications:
+ *
+ * JRM -- 4/26/06
+ * Modified routine to allow it to operate on protected
+ * entries.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C2_unpin_entry(H5C2_t * cache_ptr,
+ void * thing)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5C2_cache_entry_t * entry_ptr;
+
+ FUNC_ENTER_NOAPI(H5C2_unpin_entry, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( thing );
+
+ entry_ptr = (H5C2_cache_entry_t *)thing;
+
+ if ( ! ( entry_ptr->is_pinned ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "Entry isn't pinned")
+ }
+
+ if ( ! ( entry_ptr->is_protected ) ) {
+
+ H5C2__UPDATE_RP_FOR_UNPIN(cache_ptr, entry_ptr, FAIL)
+ }
+
+ entry_ptr->is_pinned = FALSE;
+
+ H5C2__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr)
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_unpin_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_unprotect
+ *
+ * Purpose: Undo an H5C2_protect() call -- specifically, mark the
+ * entry as unprotected, remove it from the protected list,
+ * and give it back to the replacement policy.
+ *
+ * The TYPE and ADDR arguments must be the same as those in
+ * the corresponding call to H5C2_protect() and the THING
+ * argument must be the value returned by that call to
+ * H5C2_protect().
+ *
+ * The primary_dxpl_id and secondary_dxpl_id parameters
+ * specify the dxpl_ids used on the first write occasioned
+ * by the unprotect (primary_dxpl_id), and on all subsequent
+ * writes (secondary_dxpl_id). Since an uprotect cannot
+ * occasion a write at present, all this is moot for now.
+ * However, things change, and in any case,
+ * H5C2_flush_single_entry() needs primary_dxpl_id and
+ * secondary_dxpl_id in its parameter list.
+ *
+ * The function can't cause a read either, so the dxpl_id
+ * parameters are moot in this case as well.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * If the deleted flag is TRUE, simply remove the target entry
+ * from the cache, clear it, and free it without writing it to
+ * disk.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/2/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/21/04
+ * Updated the function for the addition of the hash table.
+ * In particular, we now add dirty entries to the tree if
+ * they aren't in the tree already.
+ *
+ * JRM -- 1/6/05
+ * Added the flags parameter, and code supporting
+ * H5C2__SET_FLUSH_MARKER_FLAG. Note that this flag is
+ * ignored unless the new entry is dirty. Also note that
+ * once the flush_marker field of an entry is set, the
+ * only way it can be reset is by being flushed.
+ *
+ * JRM -- 6/3/05
+ * Added the dirtied parameter and supporting code. This
+ * is part of an effort to move management of the is_dirty
+ * field into the cache code. This has become necessary
+ * to repair a cache coherency bug in PHDF5.
+ *
+ * JRM -- 7/5/05
+ * Added code supporting the new clear_on_unprotect field
+ * of H5C2_cache_entry_t. This change is also part of the
+ * above mentioned cache coherency bug fix in PHDF5.
+ *
+ * JRM -- 9/8/05
+ * Added the size_changed and new_size parameters and the
+ * supporting code. Since the metadata cache synchronizes
+ * on dirty bytes creation in the PHDF5 case, we must now
+ * track changes in entry size.
+ *
+ * Note that the new_size parameter is ignored unless the
+ * size_changed parameter is TRUE. In this case, the new_size
+ * must be positive.
+ *
+ * Also observe that if size_changed is TRUE, dirtied must be
+ * TRUE.
+ *
+ * JRM -- 9/23/05
+ * Moved the size_changed parameter into flags.
+ *
+ * JRM -- 3/21/06
+ * Unpdated function to pin and unpin entries as directed via
+ * the new H5C2__PIN_ENTRY_FLAG and H5C2__UNPIN_ENTRY_FLAG flags.
+ *
+ * JRM -- 5/3/06
+ * Added code to make use of the new dirtied field in
+ * H5C2_cache_entry_t. If this field is TRUE, it is the
+ * equivalent of setting the H5C2__DIRTIED_FLAG.
+ *
+ * JRM -- 3/29/07
+ * Modified function to allow a entry to be protected
+ * more than once if the entry is protected read only.
+ *
+ * Also added sanity checks using the new is_read_only and
+ * ro_ref_count parameters.
+ *
+ * JRM -- 9/8/07
+ * Revised function for the new metadata cache API. The
+ * function lost its pointer to H5F_t (now supplied via
+ * *cache_ptr), and one of its dxpl ids. Also internal
+ * changes supporting the revised API.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C2_unprotect(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ const H5C2_class_t * type,
+ haddr_t addr,
+ void * thing,
+ unsigned int flags,
+ size_t new_size)
+{
+ /* const char * fcn_name = "H5C2_unprotect()"; */
+ hbool_t deleted;
+ hbool_t dirtied;
+ hbool_t set_flush_marker;
+ hbool_t size_changed;
+ hbool_t pin_entry;
+ hbool_t unpin_entry;
+#ifdef H5_HAVE_PARALLEL
+ hbool_t clear_entry = FALSE;
+#endif /* H5_HAVE_PARALLEL */
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5C2_cache_entry_t * entry_ptr;
+ H5C2_cache_entry_t * test_entry_ptr;
+
+ FUNC_ENTER_NOAPI(H5C2_unprotect, FAIL)
+
+ deleted = ( (flags & H5C2__DELETED_FLAG) != 0 );
+ dirtied = ( (flags & H5C2__DIRTIED_FLAG) != 0 );
+ set_flush_marker = ( (flags & H5C2__SET_FLUSH_MARKER_FLAG) != 0 );
+ size_changed = ( (flags & H5C2__SIZE_CHANGED_FLAG) != 0 );
+ pin_entry = ( (flags & H5C2__PIN_ENTRY_FLAG) != 0 );
+ unpin_entry = ( (flags & H5C2__UNPIN_ENTRY_FLAG) != 0 );
+
+ /* Changing the size of an entry dirties it. Thus, set the
+ * dirtied flag if the size_changed flag is set.
+ */
+
+ dirtied |= size_changed;
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( cache_ptr->skip_file_checks || cache_ptr->f );
+ HDassert( type );
+ HDassert( H5F_addr_defined(addr) );
+ HDassert( thing );
+ HDassert( ( size_changed == TRUE ) || ( size_changed == FALSE ) );
+ HDassert( ( ! size_changed ) || ( dirtied ) );
+ HDassert( ( ! size_changed ) || ( new_size > 0 ) );
+ HDassert( ! ( pin_entry && unpin_entry ) );
+
+ entry_ptr = (H5C2_cache_entry_t *)thing;
+
+ HDassert( entry_ptr->addr == addr );
+ HDassert( entry_ptr->type == type );
+
+ /* also set the dirtied variable if the dirtied field is set in
+ * the entry.
+ */
+ dirtied |= entry_ptr->dirtied;
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+
+ /* if the entry has multiple read only protects, just decrement
+ * the ro_ref_counter. Don't actually unprotect until the ref count
+ * drops to zero.
+ */
+ if ( entry_ptr->ro_ref_count > 1 ) {
+
+ HDassert( entry_ptr->is_protected );
+ HDassert( entry_ptr->is_read_only );
+
+ if ( dirtied ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "Read only entry modified(1)??")
+ }
+
+ (entry_ptr->ro_ref_count)--;
+
+ /* Pin or unpin the entry as requested. */
+ if ( pin_entry ) {
+
+ if ( entry_ptr->is_pinned ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, \
+ "Entry already pinned???")
+ }
+ entry_ptr->is_pinned = TRUE;
+ H5C2__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr)
+
+ } else if ( unpin_entry ) {
+
+ if ( ! ( entry_ptr->is_pinned ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, \
+ "Entry already unpinned???")
+ }
+ entry_ptr->is_pinned = FALSE;
+ H5C2__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr)
+
+ }
+
+ } else {
+
+ if ( entry_ptr->is_read_only ) {
+
+ HDassert( entry_ptr->ro_ref_count == 1 );
+
+ if ( dirtied ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "Read only entry modified(2)??")
+ }
+
+ entry_ptr->is_read_only = FALSE;
+ entry_ptr->ro_ref_count = 0;
+ }
+
+#ifdef H5_HAVE_PARALLEL
+ /* When the H5C2 code is used to implement the metadata cache in the
+ * PHDF5 case, only the cache on process 0 is allowed to write to file.
+ * All the other metadata caches must hold dirty entries until they
+ * are told that the entries are clean.
+ *
+ * The clear_on_unprotect flag in the H5C2_cache_entry_t structure
+ * exists to deal with the case in which an entry is protected when
+ * its cache receives word that the entry is now clean. In this case,
+ * the clear_on_unprotect flag is set, and the entry is flushed with
+ * the H5C2__FLUSH_CLEAR_ONLY_FLAG.
+ *
+ * All this is a bit awkward, but until the metadata cache entries
+ * are contiguous, with only one dirty flag, we have to let the supplied
+ * functions deal with the reseting the is_dirty flag.
+ */
+ if ( entry_ptr->clear_on_unprotect ) {
+
+ HDassert( entry_ptr->is_dirty );
+
+ entry_ptr->clear_on_unprotect = FALSE;
+
+ if ( ! dirtied ) {
+
+ clear_entry = TRUE;
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ if ( ! (entry_ptr->is_protected) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "Entry already unprotected??")
+ }
+
+ /* mark the entry as dirty if appropriate */
+ entry_ptr->is_dirty = ( (entry_ptr->is_dirty) || dirtied );
+
+ /* update for change in entry size if necessary */
+ if ( ( size_changed ) && ( entry_ptr->size != new_size ) ) {
+
+ /* update the protected list */
+ H5C2__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr->pl_len), \
+ (cache_ptr->pl_size), \
+ (entry_ptr->size), (new_size));
+
+ /* update the hash table */
+ H5C2__UPDATE_INDEX_FOR_SIZE_CHANGE((cache_ptr), (entry_ptr->size),\
+ (new_size));
+
+ /* if the entry is in the skip list, update that too */
+ if ( entry_ptr->in_slist ) {
+
+ H5C2__UPDATE_SLIST_FOR_SIZE_CHANGE((cache_ptr), \
+ (entry_ptr->size),\
+ (new_size));
+ }
+
+ /* update statistics just before changing the entry size */
+ H5C2__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE((cache_ptr), (entry_ptr), \
+ (new_size));
+
+ /* finally, update the entry size proper */
+ entry_ptr->size = new_size;
+ }
+
+ /* Pin or unpin the entry as requested. */
+ if ( pin_entry ) {
+
+ if ( entry_ptr->is_pinned ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, \
+ "Entry already pinned???")
+ }
+ entry_ptr->is_pinned = TRUE;
+ H5C2__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr)
+
+ } else if ( unpin_entry ) {
+
+ if ( ! ( entry_ptr->is_pinned ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, \
+ "Entry already unpinned???")
+ }
+ entry_ptr->is_pinned = FALSE;
+ H5C2__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr)
+
+ }
+
+ /* H5C2__UPDATE_RP_FOR_UNPROTECT will places the unprotected entry on
+ * the pinned entry list if entry_ptr->is_pined is TRUE.
+ */
+ H5C2__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, FAIL)
+
+ entry_ptr->is_protected = FALSE;
+
+ /* if the entry is dirty, 'or' its flush_marker with the set flush flag,
+ * and then add it to the skip list if it isn't there already.
+ */
+
+ if ( entry_ptr->is_dirty ) {
+
+ entry_ptr->flush_marker |= set_flush_marker;
+
+ if ( ! (entry_ptr->in_slist) ) {
+
+ H5C2__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
+ }
+ }
+
+ /* this implementation of the "deleted" option is a bit inefficient, as
+ * we re-insert the entry to be deleted into the replacement policy
+ * data structures, only to remove them again. Depending on how often
+ * we do this, we may want to optimize a bit.
+ *
+ * On the other hand, this implementation is reasonably clean, and
+ * makes good use of existing code.
+ * JRM - 5/19/04
+ */
+ if ( deleted ) {
+
+ /* we can't delete a pinned entry */
+ HDassert ( ! (entry_ptr->is_pinned ) );
+
+ /* verify that the target entry is in the cache. */
+
+ H5C2__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL)
+
+ if ( test_entry_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "entry not in hash table?!?.")
+ }
+ else if ( test_entry_ptr != entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "hash table contains multiple entries for addr?!?.")
+ }
+
+ if ( H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ type,
+ addr,
+ (H5C2__FLUSH_CLEAR_ONLY_FLAG |
+ H5C2__FLUSH_INVALIDATE_FLAG),
+ TRUE) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Can't flush.")
+ }
+ }
+#ifdef H5_HAVE_PARALLEL
+ else if ( clear_entry ) {
+
+ /* verify that the target entry is in the cache. */
+
+ H5C2__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL)
+
+ if ( test_entry_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "entry not in hash table?!?.")
+ }
+ else if ( test_entry_ptr != entry_ptr ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \
+ "hash table contains multiple entries for addr?!?.")
+ }
+
+ if ( H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ type,
+ addr,
+ H5C2__FLUSH_CLEAR_ONLY_FLAG,
+ TRUE) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Can't clear.")
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+ }
+
+ H5C2__UPDATE_STATS_FOR_UNPROTECT(cache_ptr)
+
+done:
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+ if ( H5C2_validate_lru_list(cache_ptr) < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "LRU sanity check failed.\n");
+ }
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_unprotect() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_validate_resize_config()
+ *
+ * Purpose: Run a sanity check on the specified sections of the
+ * provided instance of struct H5C2_auto_size_ctl_t.
+ *
+ * Do nothing and return SUCCEED if no errors are detected,
+ * and flag an error and return FAIL otherwise.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 3/23/05
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+herr_t
+H5C2_validate_resize_config(H5C2_auto_size_ctl_t * config_ptr,
+ unsigned int tests)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5C2_validate_resize_config, FAIL)
+
+ if ( config_ptr == NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "NULL config_ptr on entry.")
+ }
+
+ if ( config_ptr->version != H5C2__CURR_AUTO_SIZE_CTL_VER ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown config version.")
+ }
+
+
+ if ( (tests & H5C2_RESIZE_CFG__VALIDATE_GENERAL) != 0 ) {
+
+ if ( ( config_ptr->set_initial_size != TRUE ) &&
+ ( config_ptr->set_initial_size != FALSE ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "set_initial_size must be either TRUE or FALSE");
+ }
+
+ if ( config_ptr->max_size > H5C2__MAX_MAX_CACHE_SIZE ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "max_size too big");
+ }
+
+ if ( config_ptr->min_size < H5C2__MIN_MAX_CACHE_SIZE ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "min_size too small");
+ }
+
+ if ( config_ptr->min_size > config_ptr->max_size ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "min_size > max_size");
+ }
+
+ if ( ( config_ptr->set_initial_size ) &&
+ ( ( config_ptr->initial_size < config_ptr->min_size ) ||
+ ( config_ptr->initial_size > config_ptr->max_size ) ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "initial_size must be in the interval [min_size, max_size]");
+ }
+
+ if ( ( config_ptr->min_clean_fraction < 0.0 ) ||
+ ( config_ptr->min_clean_fraction > 1.0 ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "min_clean_fraction must be in the interval [0.0, 1.0]");
+ }
+
+ if ( config_ptr->epoch_length < H5C2__MIN_AR_EPOCH_LENGTH ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "epoch_length too small");
+ }
+
+ if ( config_ptr->epoch_length > H5C2__MAX_AR_EPOCH_LENGTH ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "epoch_length too big");
+ }
+ } /* H5C2_RESIZE_CFG__VALIDATE_GENERAL */
+
+
+ if ( (tests & H5C2_RESIZE_CFG__VALIDATE_INCREMENT) != 0 ) {
+
+ if ( ( config_ptr->incr_mode != H5C2_incr__off ) &&
+ ( config_ptr->incr_mode != H5C2_incr__threshold ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Invalid incr_mode");
+ }
+
+ if ( config_ptr->incr_mode == H5C2_incr__threshold ) {
+
+ if ( ( config_ptr->lower_hr_threshold < 0.0 ) ||
+ ( config_ptr->lower_hr_threshold > 1.0 ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "lower_hr_threshold must be in the range [0.0, 1.0]");
+ }
+
+ if ( config_ptr->increment < 1.0 ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "increment must be greater than or equal to 1.0");
+ }
+
+ if ( ( config_ptr->apply_max_increment != TRUE ) &&
+ ( config_ptr->apply_max_increment != FALSE ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "apply_max_increment must be either TRUE or FALSE");
+ }
+
+ /* no need to check max_increment, as it is a size_t,
+ * and thus must be non-negative.
+ */
+ } /* H5C2_incr__threshold */
+
+ } /* H5C2_RESIZE_CFG__VALIDATE_INCREMENT */
+
+
+ if ( (tests & H5C2_RESIZE_CFG__VALIDATE_DECREMENT) != 0 ) {
+
+ if ( ( config_ptr->decr_mode != H5C2_decr__off ) &&
+ ( config_ptr->decr_mode != H5C2_decr__threshold ) &&
+ ( config_ptr->decr_mode != H5C2_decr__age_out ) &&
+ ( config_ptr->decr_mode != H5C2_decr__age_out_with_threshold )
+ ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Invalid decr_mode");
+ }
+
+ if ( config_ptr->decr_mode == H5C2_decr__threshold ) {
+
+ if ( config_ptr->upper_hr_threshold > 1.0 ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "upper_hr_threshold must be <= 1.0");
+ }
+
+ if ( ( config_ptr->decrement > 1.0 ) ||
+ ( config_ptr->decrement < 0.0 ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "decrement must be in the interval [0.0, 1.0]");
+ }
+
+ /* no need to check max_decrement as it is a size_t
+ * and thus must be non-negative.
+ */
+ } /* H5C2_decr__threshold */
+
+ if ( ( config_ptr->decr_mode == H5C2_decr__age_out ) ||
+ ( config_ptr->decr_mode == H5C2_decr__age_out_with_threshold )
+ ) {
+
+ if ( config_ptr->epochs_before_eviction < 1 ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "epochs_before_eviction must be positive");
+ }
+
+ if ( config_ptr->epochs_before_eviction > H5C2__MAX_EPOCH_MARKERS ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "epochs_before_eviction too big");
+ }
+
+ if ( ( config_ptr->apply_empty_reserve != TRUE ) &&
+ ( config_ptr->apply_empty_reserve != FALSE ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "apply_empty_reserve must be either TRUE or FALSE");
+ }
+
+ if ( ( config_ptr->apply_empty_reserve ) &&
+ ( ( config_ptr->empty_reserve > 1.0 ) ||
+ ( config_ptr->empty_reserve < 0.0 ) ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "empty_reserve must be in the interval [0.0, 1.0]");
+ }
+
+ /* no need to check max_decrement as it is a size_t
+ * and thus must be non-negative.
+ */
+ } /* H5C2_decr__age_out || H5C2_decr__age_out_with_threshold */
+
+ if ( config_ptr->decr_mode == H5C2_decr__age_out_with_threshold ) {
+
+ if ( ( config_ptr->upper_hr_threshold > 1.0 ) ||
+ ( config_ptr->upper_hr_threshold < 0.0 ) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "upper_hr_threshold must be in the interval [0.0, 1.0]");
+ }
+ } /* H5C2_decr__age_out_with_threshold */
+
+ } /* H5C2_RESIZE_CFG__VALIDATE_DECREMENT */
+
+
+ if ( (tests & H5C2_RESIZE_CFG__VALIDATE_INTERACTIONS) != 0 ) {
+
+ if ( ( config_ptr->incr_mode == H5C2_incr__threshold )
+ &&
+ ( ( config_ptr->decr_mode == H5C2_decr__threshold )
+ ||
+ ( config_ptr->decr_mode == H5C2_decr__age_out_with_threshold )
+ )
+ &&
+ ( config_ptr->lower_hr_threshold
+ >=
+ config_ptr->upper_hr_threshold
+ )
+ ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \
+ "conflicting threshold fields in config.")
+ }
+ } /* H5C2_RESIZE_CFG__VALIDATE_INTERACTIONS */
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_validate_resize_config() */
+
+
+/*************************************************************************/
+/**************************** Private Functions: *************************/
+/*************************************************************************/
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2__auto_adjust_cache_size
+ *
+ * Purpose: Obtain the current full cache hit rate, and compare it
+ * with the hit rate thresholds for modifying cache size.
+ * If one of the thresholds has been crossed, adjusts the
+ * size of the cache accordingly.
+ *
+ * The function then resets the full cache hit rate
+ * statistics, and exits.
+ *
+ * Return: Non-negative on success/Negative on failure or if there was
+ * an attempt to flush a protected item.
+ *
+ *
+ * Programmer: John Mainzer, 10/7/04
+ *
+ * Modifications:
+ *
+ * JRM -- 11/18/04
+ * Major re-write to support ageout method of cache size
+ * reduction, and to adjust to changes in the
+ * H5C2_auto_size_ctl_t structure.
+ *
+ * JRM -- 9/8/07
+ * Reworked to accomodate cache API changes needed to
+ * support metadata journaling. Mostly, this involved
+ * removing a bunch of parameters that used to be
+ * passed through to other calls, and are no longer
+ * needed.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static herr_t
+H5C2__auto_adjust_cache_size(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ hbool_t write_permitted)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ herr_t result;
+ hbool_t inserted_epoch_marker = FALSE;
+ size_t new_max_cache_size = 0;
+ size_t old_max_cache_size = 0;
+ size_t new_min_clean_size = 0;
+ size_t old_min_clean_size = 0;
+ double hit_rate;
+ enum H5C2_resize_status status = in_spec2; /* will change if needed */
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2__auto_adjust_cache_size)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( cache_ptr->cache_accesses >=
+ (cache_ptr->resize_ctl).epoch_length );
+ HDassert( 0.0 <= (cache_ptr->resize_ctl).min_clean_fraction );
+ HDassert( (cache_ptr->resize_ctl).min_clean_fraction <= 100.0 );
+
+ if ( !cache_ptr->resize_enabled ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Auto cache resize disabled.")
+ }
+
+ HDassert( ( (cache_ptr->resize_ctl).incr_mode != H5C2_incr__off ) || \
+ ( (cache_ptr->resize_ctl).decr_mode != H5C2_decr__off ) );
+
+ if ( H5C2_get_cache_hit_rate(cache_ptr, &hit_rate) != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't get hit rate.")
+ }
+
+ HDassert( ( 0.0 <= hit_rate ) && ( hit_rate <= 1.0 ) );
+
+ switch ( (cache_ptr->resize_ctl).incr_mode )
+ {
+ case H5C2_incr__off:
+ if ( cache_ptr->size_increase_possible ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "size_increase_possible but H5C2_incr__off?!?!?")
+ }
+ break;
+
+ case H5C2_incr__threshold:
+ if ( hit_rate < (cache_ptr->resize_ctl).lower_hr_threshold ) {
+
+ if ( ! cache_ptr->size_increase_possible ) {
+
+ status = increase_disabled2;
+
+ } else if ( cache_ptr->max_cache_size >=
+ (cache_ptr->resize_ctl).max_size ) {
+
+ HDassert( cache_ptr->max_cache_size == \
+ (cache_ptr->resize_ctl).max_size );
+ status = at_max_size2;
+
+ } else if ( ! cache_ptr->cache_full ) {
+
+ status = not_full2;
+
+ } else {
+
+ new_max_cache_size = (size_t)
+ (((double)(cache_ptr->max_cache_size)) *
+ (cache_ptr->resize_ctl).increment);
+
+ /* clip to max size if necessary */
+ if ( new_max_cache_size >
+ (cache_ptr->resize_ctl).max_size ) {
+
+ new_max_cache_size = (cache_ptr->resize_ctl).max_size;
+ }
+
+ /* clip to max increment if necessary */
+ if ( ( (cache_ptr->resize_ctl).apply_max_increment ) &&
+ ( (cache_ptr->max_cache_size +
+ (cache_ptr->resize_ctl).max_increment) <
+ new_max_cache_size ) ) {
+
+ new_max_cache_size = cache_ptr->max_cache_size +
+ (cache_ptr->resize_ctl).max_increment;
+ }
+
+ status = increase2;
+ }
+ }
+ break;
+
+ default:
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unknown incr_mode.")
+ }
+
+ /* If the decr_mode is either age out or age out with threshold, we
+ * must run the marker maintenance code, whether we run the size
+ * reduction code or not. We do this in two places -- here we
+ * insert a new marker if the number of active epoch markers is
+ * is less than the the current epochs before eviction, and after
+ * the ageout call, we cycle the markers.
+ *
+ * However, we can't call the ageout code or cycle the markers
+ * unless there was a full complement of markers in place on
+ * entry. The inserted_epoch_marker flag is used to track this.
+ */
+
+ if ( ( ( (cache_ptr->resize_ctl).decr_mode == H5C2_decr__age_out )
+ ||
+ ( (cache_ptr->resize_ctl).decr_mode ==
+ H5C2_decr__age_out_with_threshold
+ )
+ )
+ &&
+ ( cache_ptr->epoch_markers_active <
+ (cache_ptr->resize_ctl).epochs_before_eviction
+ )
+ ) {
+
+ result = H5C2__autoadjust__ageout__insert_new_marker(cache_ptr);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "can't insert new epoch marker.")
+
+ } else {
+
+ inserted_epoch_marker = TRUE;
+ }
+ }
+
+ /* don't run the cache size decrease code unless the cache size
+ * increase code is disabled, or the size increase code sees no need
+ * for action. In either case, status == in_spec2 at this point.
+ */
+
+ if ( status == in_spec2 ) {
+
+ switch ( (cache_ptr->resize_ctl).decr_mode )
+ {
+ case H5C2_decr__off:
+ break;
+
+ case H5C2_decr__threshold:
+ if ( hit_rate > (cache_ptr->resize_ctl).upper_hr_threshold ) {
+
+ if ( ! cache_ptr->size_decrease_possible ) {
+
+ status = decrease_disabled2;
+
+ } else if ( cache_ptr->max_cache_size <=
+ (cache_ptr->resize_ctl).min_size ) {
+
+ HDassert( cache_ptr->max_cache_size ==
+ (cache_ptr->resize_ctl).min_size );
+ status = at_min_size2;
+
+ } else {
+
+ new_max_cache_size = (size_t)
+ (((double)(cache_ptr->max_cache_size)) *
+ (cache_ptr->resize_ctl).decrement);
+
+ /* clip to min size if necessary */
+ if ( new_max_cache_size <
+ (cache_ptr->resize_ctl).min_size ) {
+
+ new_max_cache_size =
+ (cache_ptr->resize_ctl).min_size;
+ }
+
+ /* clip to max decrement if necessary */
+ if ( ( (cache_ptr->resize_ctl).apply_max_decrement ) &&
+ ( ((cache_ptr->resize_ctl).max_decrement +
+ new_max_cache_size) <
+ cache_ptr->max_cache_size ) ) {
+
+ new_max_cache_size = cache_ptr->max_cache_size -
+ (cache_ptr->resize_ctl).max_decrement;
+ }
+
+ status = decrease2;
+ }
+ }
+ break;
+
+ case H5C2_decr__age_out_with_threshold:
+ case H5C2_decr__age_out:
+ if ( ! inserted_epoch_marker ) {
+
+ if ( ! cache_ptr->size_decrease_possible ) {
+
+ status = decrease_disabled2;
+
+ } else {
+
+ result = H5C2__autoadjust__ageout(cache_ptr,
+ hit_rate,
+ &status,
+ &new_max_cache_size,
+ dxpl_id,
+ write_permitted);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "ageout code failed.")
+ }
+ }
+ }
+ break;
+
+ default:
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unknown incr_mode.")
+ }
+ }
+
+ /* cycle the epoch markers here if appropriate */
+ if ( ( ( (cache_ptr->resize_ctl).decr_mode == H5C2_decr__age_out )
+ ||
+ ( (cache_ptr->resize_ctl).decr_mode ==
+ H5C2_decr__age_out_with_threshold
+ )
+ )
+ &&
+ ( ! inserted_epoch_marker )
+ ) {
+
+ /* move last epoch marker to the head of the LRU list */
+ result = H5C2__autoadjust__ageout__cycle_epoch_marker(cache_ptr);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "error cycling epoch marker.")
+ }
+ }
+
+ if ( ( status == increase2 ) || ( status == decrease2 ) ) {
+
+ old_max_cache_size = cache_ptr->max_cache_size;
+ old_min_clean_size = cache_ptr->min_clean_size;
+
+ new_min_clean_size = (size_t)
+ ((double)new_max_cache_size *
+ ((cache_ptr->resize_ctl).min_clean_fraction));
+
+ /* new_min_clean_size is of size_t, and thus must be non-negative.
+ * Hence we have
+ *
+ * ( 0 <= new_min_clean_size ).
+ *
+ * by definition.
+ */
+ HDassert( new_min_clean_size <= new_max_cache_size );
+ HDassert( (cache_ptr->resize_ctl).min_size <= new_max_cache_size );
+ HDassert( new_max_cache_size <= (cache_ptr->resize_ctl).max_size );
+
+ cache_ptr->max_cache_size = new_max_cache_size;
+ cache_ptr->min_clean_size = new_min_clean_size;
+
+ if ( status == increase2 ) {
+
+ cache_ptr->cache_full = FALSE;
+
+ } else if ( status == decrease2 ) {
+
+ cache_ptr->size_decreased = TRUE;
+ }
+ }
+
+ if ( (cache_ptr->resize_ctl).rpt_fcn != NULL ) {
+
+ (*((cache_ptr->resize_ctl).rpt_fcn))
+ (cache_ptr,
+ H5C2__CURR_AUTO_RESIZE_RPT_FCN_VER,
+ hit_rate,
+ status,
+ old_max_cache_size,
+ new_max_cache_size,
+ old_min_clean_size,
+ new_min_clean_size);
+ }
+
+ if ( H5C2_reset_cache_hit_rate_stats(cache_ptr) != SUCCEED ) {
+
+ /* this should be impossible... */
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "H5C2_reset_cache_hit_rate_stats failed.")
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2__auto_adjust_cache_size() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2__autoadjust__ageout
+ *
+ * Purpose: Implement the ageout automatic cache size decrement
+ * algorithm. Note that while this code evicts aged out
+ * entries, the code does not change the maximum cache size.
+ * Instead, the function simply computes the new value (if
+ * any change is indicated) and reports this value in
+ * *new_max_cache_size_ptr.
+ *
+ * Return: Non-negative on success/Negative on failure or if there was
+ * an attempt to flush a protected item.
+ *
+ *
+ * Programmer: John Mainzer, 11/18/04
+ *
+ * Modifications:
+ *
+ * JRM -- 9/9/07
+ * Reworked function to support API changes in support of
+ * metadata cacheing. In essence, the change involved
+ * removal of arguments that are no longer needed by the
+ * callbacks, and thus no-longer need be passed through.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static herr_t
+H5C2__autoadjust__ageout(H5C2_t * cache_ptr,
+ double hit_rate,
+ enum H5C2_resize_status * status_ptr,
+ size_t * new_max_cache_size_ptr,
+ hid_t dxpl_id,
+ hbool_t write_permitted)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ herr_t result;
+ size_t test_size;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2__autoadjust__ageout)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( ( status_ptr ) && ( *status_ptr == in_spec2 ) );
+ HDassert( ( new_max_cache_size_ptr ) && ( *new_max_cache_size_ptr == 0 ) );
+
+ /* remove excess epoch markers if any */
+ if ( cache_ptr->epoch_markers_active >
+ (cache_ptr->resize_ctl).epochs_before_eviction ) {
+
+ result = H5C2__autoadjust__ageout__remove_excess_markers(cache_ptr);
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "can't remove excess epoch markers.")
+ }
+ }
+
+ if ( ( (cache_ptr->resize_ctl).decr_mode == H5C2_decr__age_out )
+ ||
+ ( ( (cache_ptr->resize_ctl).decr_mode ==
+ H5C2_decr__age_out_with_threshold
+ )
+ &&
+ ( hit_rate >= (cache_ptr->resize_ctl).upper_hr_threshold )
+ )
+ ) {
+
+ if ( cache_ptr->max_cache_size > (cache_ptr->resize_ctl).min_size ){
+
+ /* evict aged out cache entries if appropriate... */
+ result = H5C2__autoadjust__ageout__evict_aged_out_entries
+ (
+ dxpl_id,
+ cache_ptr,
+ write_permitted
+ );
+
+ if ( result != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "error flushing aged out entries.")
+ }
+
+ /* ... and then reduce cache size if appropriate */
+ if ( cache_ptr->index_size < cache_ptr->max_cache_size ) {
+
+ if ( (cache_ptr->resize_ctl).apply_empty_reserve ) {
+
+ test_size = (size_t)(((double)cache_ptr->index_size) /
+ (1 - (cache_ptr->resize_ctl).empty_reserve));
+
+ if ( test_size < cache_ptr->max_cache_size ) {
+
+ *status_ptr = decrease2;
+ *new_max_cache_size_ptr = test_size;
+ }
+ } else {
+
+ *status_ptr = decrease2;
+ *new_max_cache_size_ptr = cache_ptr->index_size;
+ }
+
+ if ( *status_ptr == decrease2 ) {
+
+ /* clip to min size if necessary */
+ if ( *new_max_cache_size_ptr <
+ (cache_ptr->resize_ctl).min_size ) {
+
+ *new_max_cache_size_ptr =
+ (cache_ptr->resize_ctl).min_size;
+ }
+
+ /* clip to max decrement if necessary */
+ if ( ( (cache_ptr->resize_ctl).apply_max_decrement ) &&
+ ( ((cache_ptr->resize_ctl).max_decrement +
+ *new_max_cache_size_ptr) <
+ cache_ptr->max_cache_size ) ) {
+
+ *new_max_cache_size_ptr = cache_ptr->max_cache_size -
+ (cache_ptr->resize_ctl).max_decrement;
+ }
+ }
+ }
+ } else {
+
+ *status_ptr = at_min_size2;
+ }
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2__autoadjust__ageout() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2__autoadjust__ageout__cycle_epoch_marker
+ *
+ * Purpose: Remove the oldest epoch marker from the LRU list,
+ * and reinsert it at the head of the LRU list. Also
+ * remove the epoch marker's index from the head of the
+ * ring buffer, and re-insert it at the tail of the ring
+ * buffer.
+ *
+ * Return: SUCCEED on success/FAIL on failure.
+ *
+ * Programmer: John Mainzer, 11/22/04
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static herr_t
+H5C2__autoadjust__ageout__cycle_epoch_marker(H5C2_t * cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ int i;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2__autoadjust__ageout__cycle_epoch_marker)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ if ( cache_ptr->epoch_markers_active <= 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "No active epoch markers on entry?!?!?.")
+ }
+
+ /* remove the last marker from both the ring buffer and the LRU list */
+
+ i = cache_ptr->epoch_marker_ringbuf[cache_ptr->epoch_marker_ringbuf_first];
+
+ cache_ptr->epoch_marker_ringbuf_first =
+ (cache_ptr->epoch_marker_ringbuf_first + 1) %
+ (H5C2__MAX_EPOCH_MARKERS + 1);
+
+ cache_ptr->epoch_marker_ringbuf_size -= 1;
+
+ if ( cache_ptr->epoch_marker_ringbuf_size < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer underflow.")
+ }
+
+ if ( (cache_ptr->epoch_marker_active)[i] != TRUE ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unused marker in LRU?!?")
+ }
+
+ H5C2__DLL_REMOVE((&((cache_ptr->epoch_markers)[i])), \
+ (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, \
+ (FAIL))
+
+ /* now, re-insert it at the head of the LRU list, and at the tail of
+ * the ring buffer.
+ */
+
+ HDassert( ((cache_ptr->epoch_markers)[i]).addr == (haddr_t)i );
+ HDassert( ((cache_ptr->epoch_markers)[i]).next == NULL );
+ HDassert( ((cache_ptr->epoch_markers)[i]).prev == NULL );
+
+ cache_ptr->epoch_marker_ringbuf_last =
+ (cache_ptr->epoch_marker_ringbuf_last + 1) %
+ (H5C2__MAX_EPOCH_MARKERS + 1);
+
+ (cache_ptr->epoch_marker_ringbuf)[cache_ptr->epoch_marker_ringbuf_last] = i;
+
+ cache_ptr->epoch_marker_ringbuf_size += 1;
+
+ if ( cache_ptr->epoch_marker_ringbuf_size > H5C2__MAX_EPOCH_MARKERS ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer overflow.")
+ }
+
+ H5C2__DLL_PREPEND((&((cache_ptr->epoch_markers)[i])), \
+ (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, \
+ (FAIL))
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2__autoadjust__ageout__cycle_epoch_marker() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2__autoadjust__ageout__evict_aged_out_entries
+ *
+ * Purpose: Evict clean entries in the cache that haven't
+ * been accessed for at least
+ * (cache_ptr->resize_ctl).epochs_before_eviction epochs,
+ * and flush dirty entries that haven't been accessed for
+ * that amount of time.
+ *
+ * Depending on configuration, the function will either
+ * flush or evict all such entries, or all such entries it
+ * encounters until it has freed the maximum amount of space
+ * allowed under the maximum decrement.
+ *
+ * If we are running in parallel mode, writes may not be
+ * permitted. If so, the function simply skips any dirty
+ * entries it may encounter.
+ *
+ * The function makes no attempt to maintain the minimum
+ * clean size, as there is no guarantee that the cache size
+ * will be changed.
+ *
+ * If there is no cache size change, the minimum clean size
+ * constraint will be met through a combination of clean
+ * entries and free space in the cache.
+ *
+ * If there is a cache size reduction, the minimum clean size
+ * will be re-calculated, and will be enforced the next time
+ * we have to make space in the cache.
+ *
+ * The primary_dxpl_id and secondary_dxpl_id parameters
+ * specify the dxpl_ids used depending on the value of
+ * *first_flush_ptr. The idea is to use the primary_dxpl_id
+ * on the first write in a sequence of writes, and to use
+ * the secondary_dxpl_id on all subsequent writes.
+ *
+ * This is useful in the metadata cache, but may not be
+ * needed elsewhere. If so, just use the same dxpl_id for
+ * both parameters.
+ *
+ * Observe that this function cannot occasion a read.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 11/22/04
+ *
+ * Modifications:
+ *
+ * JRM -- 9/9/07
+ * Reworked function to support API changes in support of
+ * metadata cacheing. In essence, the change involved
+ * removal of arguments that are no longer needed by the
+ * callbacks, and thus no-longer need be passed through.
+ *
+ * JRM -- 10/13/07
+ * Reworked code to allow it to the function to handle the
+ * case in which the LRU list is modified out from under the
+ * function by a serialize function. This can happen if
+ * the serialize function associated with the entry being
+ * flushed either accesses the next item in the LRU list,
+ * or (as Quincey assures me is impossible), it accesses
+ * an entry not currently in cache, causing the eviction
+ * of the next entry in the LRU.
+ *
+ * We handle this situation by detecting it, and restarting
+ * the scan of the LRU when it occurs.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static herr_t
+H5C2__autoadjust__ageout__evict_aged_out_entries(hid_t dxpl_id,
+ H5C2_t * cache_ptr,
+ hbool_t write_permitted)
+{
+ /* const char * fcn_name =
+ "H5C2__autoadjust__ageout__evict_aged_out_entries()"; */
+ herr_t ret_value = SUCCEED; /* Return value */
+ herr_t result;
+ size_t eviction_size_limit;
+ size_t bytes_evicted = 0;
+ hbool_t prev_is_dirty = FALSE;
+ H5C2_cache_entry_t * entry_ptr;
+ H5C2_cache_entry_t * next_ptr;
+ H5C2_cache_entry_t * prev_ptr;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2__autoadjust__ageout__evict_aged_out_entries)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ /* if there is a limit on the amount that the cache size can be decrease
+ * in any one round of the cache size reduction algorithm, load that
+ * limit into eviction_size_limit. Otherwise, set eviction_size_limit
+ * to the equivalent of infinity. The current size of the index will
+ * do nicely.
+ */
+ if ( (cache_ptr->resize_ctl).apply_max_decrement ) {
+
+ eviction_size_limit = (cache_ptr->resize_ctl).max_decrement;
+
+ } else {
+
+ eviction_size_limit = cache_ptr->index_size; /* i.e. infinity */
+ }
+
+ if ( write_permitted ) {
+
+ entry_ptr = cache_ptr->LRU_tail_ptr;
+
+ while ( ( entry_ptr != NULL ) &&
+ ( (entry_ptr->type)->id != H5C2__EPOCH_MARKER_TYPE ) &&
+ ( bytes_evicted < eviction_size_limit ) )
+ {
+ HDassert( ! (entry_ptr->is_protected) );
+
+ next_ptr = entry_ptr->next;
+ prev_ptr = entry_ptr->prev;
+
+ if ( prev_ptr != NULL ) {
+
+ prev_is_dirty = prev_ptr->is_dirty;
+ }
+
+ if ( entry_ptr->is_dirty ) {
+
+ result = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ H5C2__NO_FLAGS_SET,
+ FALSE);
+ } else {
+
+ bytes_evicted += entry_ptr->size;
+
+ result = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ H5C2__FLUSH_INVALIDATE_FLAG,
+ TRUE);
+ }
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unable to flush entry")
+ }
+
+ if ( prev_ptr != NULL ) {
+
+ if ( prev_ptr->magic != H5C2__H5C2_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 if ( ( 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.
+ */
+ entry_ptr = cache_ptr->LRU_tail_ptr;
+
+ } else {
+
+ entry_ptr = prev_ptr;
+
+ }
+ } else {
+
+ entry_ptr = NULL;
+
+ }
+ } /* end while */
+
+ /* for now at least, don't bother to maintain the minimum clean size,
+ * as the cache should now be less than its maximum size. Due to
+ * the vaguries of the cache size reduction algorthim, we may not
+ * reduce the size of the cache.
+ *
+ * If we do, we will calculate a new minimum clean size, which will
+ * be enforced the next time we try to make space in the cache.
+ *
+ * If we don't, no action is necessary, as we have just evicted and/or
+ * or flushed a bunch of entries and therefore the sum of the clean
+ * and free space in the cache must be greater than or equal to the
+ * min clean space requirement (assuming that requirement was met on
+ * entry).
+ */
+
+ } else /* ! write_permitted */ {
+
+ /* since we are not allowed to write, all we can do is evict
+ * any clean entries that we may encounter before we either
+ * hit the eviction size limit, or encounter the epoch marker.
+ *
+ * If we are operating read only, this isn't an issue, as there
+ * will not be any dirty entries.
+ *
+ * If we are operating in R/W mode, all the dirty entries we
+ * skip will be flushed the next time we attempt to make space
+ * when writes are permitted. This may have some local
+ * performance implications, but it shouldn't cause any net
+ * slowdown.
+ */
+
+ HDassert( H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS );
+
+ entry_ptr = cache_ptr->LRU_tail_ptr;
+
+ while ( ( entry_ptr != NULL ) &&
+ ( (entry_ptr->type)->id != H5C2__EPOCH_MARKER_TYPE ) &&
+ ( bytes_evicted < eviction_size_limit ) )
+ {
+ HDassert( ! (entry_ptr->is_protected) );
+
+ prev_ptr = entry_ptr->prev;
+
+ if ( ! (entry_ptr->is_dirty) ) {
+
+ result = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ H5C2__FLUSH_INVALIDATE_FLAG,
+ TRUE);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unable to flush clean entry")
+ }
+ }
+ /* just skip the entry if it is dirty, as we can't do
+ * anything with it now since we can't write.
+ *
+ * Since all entries are clean, serialize() will no be called,
+ * and thus we needn't test to see if the LRU has been changed
+ * out from under us.
+ */
+
+ entry_ptr = prev_ptr;
+
+ } /* end while */
+ }
+
+ if ( cache_ptr->index_size < cache_ptr->max_cache_size ) {
+
+ cache_ptr->cache_full = FALSE;
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2__autoadjust__ageout__evict_aged_out_entries() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2__autoadjust__ageout__insert_new_marker
+ *
+ * Purpose: Find an unused marker cache entry, mark it as used, and
+ * insert it at the head of the LRU list. Also add the
+ * marker's index in the epoch_markers array.
+ *
+ * Return: SUCCEED on success/FAIL on failure.
+ *
+ * Programmer: John Mainzer, 11/19/04
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static herr_t
+H5C2__autoadjust__ageout__insert_new_marker(H5C2_t * cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ int i;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2__autoadjust__ageout__insert_new_marker)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ if ( cache_ptr->epoch_markers_active >=
+ (cache_ptr->resize_ctl).epochs_before_eviction ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Already have a full complement of markers.")
+ }
+
+ /* find an unused marker */
+ i = 0;
+ while ( ( (cache_ptr->epoch_marker_active)[i] ) &&
+ ( i < H5C2__MAX_EPOCH_MARKERS ) )
+ {
+ i++;
+ }
+
+ HDassert( i < H5C2__MAX_EPOCH_MARKERS );
+
+ if ( (cache_ptr->epoch_marker_active)[i] != FALSE ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't find unused marker.")
+ }
+
+ HDassert( ((cache_ptr->epoch_markers)[i]).addr == (haddr_t)i );
+ HDassert( ((cache_ptr->epoch_markers)[i]).next == NULL );
+ HDassert( ((cache_ptr->epoch_markers)[i]).prev == NULL );
+
+ (cache_ptr->epoch_marker_active)[i] = TRUE;
+
+ cache_ptr->epoch_marker_ringbuf_last =
+ (cache_ptr->epoch_marker_ringbuf_last + 1) %
+ (H5C2__MAX_EPOCH_MARKERS + 1);
+
+ (cache_ptr->epoch_marker_ringbuf)[cache_ptr->epoch_marker_ringbuf_last] = i;
+
+ cache_ptr->epoch_marker_ringbuf_size += 1;
+
+ if ( cache_ptr->epoch_marker_ringbuf_size > H5C2__MAX_EPOCH_MARKERS ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer overflow.")
+ }
+
+ H5C2__DLL_PREPEND((&((cache_ptr->epoch_markers)[i])), \
+ (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, \
+ (FAIL))
+
+ cache_ptr->epoch_markers_active += 1;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2__autoadjust__ageout__insert_new_marker() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2__autoadjust__ageout__remove_all_markers
+ *
+ * Purpose: Remove all epoch markers from the LRU list and mark them
+ * as inactive.
+ *
+ * Return: SUCCEED on success/FAIL on failure.
+ *
+ * Programmer: John Mainzer, 11/22/04
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static herr_t
+H5C2__autoadjust__ageout__remove_all_markers(H5C2_t * cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ int i;
+ int ring_buf_index;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2__autoadjust__ageout__remove_all_markers)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ while ( cache_ptr->epoch_markers_active > 0 )
+ {
+ /* get the index of the last epoch marker in the LRU list
+ * and remove it from the ring buffer.
+ */
+
+ ring_buf_index = cache_ptr->epoch_marker_ringbuf_first;
+ i = (cache_ptr->epoch_marker_ringbuf)[ring_buf_index];
+
+ cache_ptr->epoch_marker_ringbuf_first =
+ (cache_ptr->epoch_marker_ringbuf_first + 1) %
+ (H5C2__MAX_EPOCH_MARKERS + 1);
+
+ cache_ptr->epoch_marker_ringbuf_size -= 1;
+
+ if ( cache_ptr->epoch_marker_ringbuf_size < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer underflow.")
+ }
+
+ if ( (cache_ptr->epoch_marker_active)[i] != TRUE ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unused marker in LRU?!?")
+ }
+
+ /* remove the epoch marker from the LRU list */
+ H5C2__DLL_REMOVE((&((cache_ptr->epoch_markers)[i])), \
+ (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, \
+ (FAIL))
+
+ /* mark the epoch marker as unused. */
+ (cache_ptr->epoch_marker_active)[i] = FALSE;
+
+ HDassert( ((cache_ptr->epoch_markers)[i]).addr == (haddr_t)i );
+ HDassert( ((cache_ptr->epoch_markers)[i]).next == NULL );
+ HDassert( ((cache_ptr->epoch_markers)[i]).prev == NULL );
+
+ /* decrement the number of active epoch markers */
+ cache_ptr->epoch_markers_active -= 1;
+
+ HDassert( cache_ptr->epoch_markers_active == \
+ cache_ptr->epoch_marker_ringbuf_size );
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2__autoadjust__ageout__remove_all_markers() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2__autoadjust__ageout__remove_excess_markers
+ *
+ * Purpose: Remove epoch markers from the end of the LRU list and
+ * mark them as inactive until the number of active markers
+ * equals the the current value of
+ * (cache_ptr->resize_ctl).epochs_before_eviction.
+ *
+ * Return: SUCCEED on success/FAIL on failure.
+ *
+ * Programmer: John Mainzer, 11/19/04
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static herr_t
+H5C2__autoadjust__ageout__remove_excess_markers(H5C2_t * cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ int i;
+ int ring_buf_index;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2__autoadjust__ageout__remove_excess_markers)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ if ( cache_ptr->epoch_markers_active <=
+ (cache_ptr->resize_ctl).epochs_before_eviction ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "no excess markers on entry.")
+ }
+
+ while ( cache_ptr->epoch_markers_active >
+ (cache_ptr->resize_ctl).epochs_before_eviction )
+ {
+ /* get the index of the last epoch marker in the LRU list
+ * and remove it from the ring buffer.
+ */
+
+ ring_buf_index = cache_ptr->epoch_marker_ringbuf_first;
+ i = (cache_ptr->epoch_marker_ringbuf)[ring_buf_index];
+
+ cache_ptr->epoch_marker_ringbuf_first =
+ (cache_ptr->epoch_marker_ringbuf_first + 1) %
+ (H5C2__MAX_EPOCH_MARKERS + 1);
+
+ cache_ptr->epoch_marker_ringbuf_size -= 1;
+
+ if ( cache_ptr->epoch_marker_ringbuf_size < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer underflow.")
+ }
+
+ if ( (cache_ptr->epoch_marker_active)[i] != TRUE ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unused marker in LRU?!?")
+ }
+
+ /* remove the epoch marker from the LRU list */
+ H5C2__DLL_REMOVE((&((cache_ptr->epoch_markers)[i])), \
+ (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, \
+ (FAIL))
+
+ /* mark the epoch marker as unused. */
+ (cache_ptr->epoch_marker_active)[i] = FALSE;
+
+ HDassert( ((cache_ptr->epoch_markers)[i]).addr == (haddr_t)i );
+ HDassert( ((cache_ptr->epoch_markers)[i]).next == NULL );
+ HDassert( ((cache_ptr->epoch_markers)[i]).prev == NULL );
+
+ /* decrement the number of active epoch markers */
+ cache_ptr->epoch_markers_active -= 1;
+
+ HDassert( cache_ptr->epoch_markers_active == \
+ cache_ptr->epoch_marker_ringbuf_size );
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2__autoadjust__ageout__remove_excess_markers() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C2_flush_invalidate_cache
+ *
+ * Purpose: Flush and destroy the entries contained in the target
+ * cache.
+ *
+ * If the cache contains protected entries, the function will
+ * fail, as protected entries cannot be either flushed or
+ * destroyed. However all unprotected entries should be
+ * flushed and destroyed before the function returns failure.
+ *
+ * While pinned entries can usually be flushed, they cannot
+ * be destroyed. However, they should be unpinned when all
+ * the entries that reference them have been destroyed (thus
+ * reduding the pinned entry's reference count to 0, allowing
+ * it to be unpinned).
+ *
+ * If pinned entries are present, the function makes repeated
+ * passes through the cache, flushing all dirty entries
+ * (including the pinned dirty entries where permitted) and
+ * destroying all unpinned entries. This process is repeated
+ * until either the cache is empty, or the number of pinned
+ * entries stops decreasing on each pass.
+ *
+ * The primary_dxpl_id and secondary_dxpl_id parameters
+ * specify the dxpl_ids used on the first write occasioned
+ * by the flush (primary_dxpl_id), and on all subsequent
+ * writes (secondary_dxpl_id).
+ *
+ * Return: Non-negative on success/Negative on failure or if there was
+ * a request to flush all items and something was protected.
+ *
+ * Programmer: John Mainzer
+ * 3/24/065
+ *
+ * Modifications:
+ *
+ * To support the fractal heap, the cache must now deal with
+ * entries being dirtied, resized, and/or renamed inside
+ * flush callbacks. Updated function to support this.
+ *
+ * -- JRM 8/27/06
+ *
+ * Reworked argument list and code to reflect the
+ * removal of the secondary dxpl id, and the decision
+ * to store f in H5C2_t, removing the need to pass it
+ * in all the time.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C2_flush_invalidate_cache(hid_t dxpl_id,
+ H5C2_t * cache_ptr,
+ unsigned flags)
+{
+ /* const char * fcn_name = "H5C2_flush_invalidate_cache()"; */
+ herr_t status;
+ herr_t ret_value = SUCCEED;
+ hbool_t done = FALSE;
+ int32_t protected_entries = 0;
+ int32_t i;
+ int32_t cur_pel_len;
+ int32_t old_pel_len;
+ int32_t passes = 0;
+ unsigned cooked_flags;
+ H5SL_node_t * node_ptr = NULL;
+ H5C2_cache_entry_t * entry_ptr = NULL;
+ H5C2_cache_entry_t * next_entry_ptr = NULL;
+#if H5C2_DO_SANITY_CHECKS
+ int64_t actual_slist_len = 0;
+ int64_t initial_slist_len = 0;
+ size_t actual_slist_size = 0;
+ size_t initial_slist_size = 0;
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ FUNC_ENTER_NOAPI(H5C2_flush_invalidate_cache, FAIL)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( cache_ptr->skip_file_checks || cache_ptr->f );
+ HDassert( cache_ptr->slist_ptr );
+
+ /* Filter out the flags that are not relevant to the flush/invalidate.
+ * At present, only the H5C2__FLUSH_CLEAR_ONLY_FLAG is kept.
+ */
+ cooked_flags = flags & H5C2__FLUSH_CLEAR_ONLY_FLAG;
+
+ /* remove ageout markers if present */
+ if ( cache_ptr->epoch_markers_active > 0 ) {
+
+ status = H5C2__autoadjust__ageout__remove_all_markers(cache_ptr);
+
+ if ( status != SUCCEED ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "error removing all epoch markers.")
+ }
+ }
+
+ /* The flush proceedure here is a bit strange.
+ *
+ * In the outer while loop we make at least one pass through the
+ * cache, and then repeat until either all the pinned entries
+ * unpin themselves, or until the number of pinned entries stops
+ * declining. In this later case, we scream and die.
+ *
+ * Since the fractal heap can dirty, resize, and/or rename entries
+ * in is flush callback, it is possible that the cache will still
+ * contain dirty entries at this point. If so, we must make up to
+ * H5C2__MAX_PASSES_ON_FLUSH more passes through the skip list
+ * to allow it to empty. If is is not empty at this point, we again
+ * scream and die.
+ *
+ * Further, since clean entries can be dirtied, resized, and/or renamed
+ * as the result of a flush call back (either the entries own, or that
+ * for some other cache entry), we can no longer promise to flush
+ * the cache entries in increasing address order.
+ *
+ * Instead, we just do the best we can -- making a pass through
+ * the skip list, and then a pass through the "clean" entries, and
+ * then repeating as needed. Thus it is quite possible that an
+ * entry will be evicted from the cache only to be re-loaded later
+ * in the flush process (From what Quincey tells me, the pin
+ * mechanism makes this impossible, but even it it is true now,
+ * we shouldn't count on it in the future.)
+ *
+ * The bottom line is that entries will probably be flushed in close
+ * to increasing address order, but there are no guarantees.
+ */
+
+ cur_pel_len = cache_ptr->pel_len;
+ old_pel_len = cache_ptr->pel_len;
+
+ while ( ! done )
+ {
+ /* first, try to flush-destroy any dirty entries. Do this by
+ * making a scan through the slist. Note that new dirty entries
+ * may be created by the flush call backs. Thus it is possible
+ * that the slist will not be empty after we finish the scan.
+ */
+
+ if ( cache_ptr->slist_len == 0 ) {
+
+ node_ptr = NULL;
+ next_entry_ptr = NULL;
+ HDassert( cache_ptr->slist_size == 0 );
+
+ } else {
+
+ node_ptr = H5SL_first(cache_ptr->slist_ptr);
+
+ if ( node_ptr == NULL ) {
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "slist_len != 0 && node_ptr == NULL");
+ }
+
+ next_entry_ptr = (H5C2_cache_entry_t *)H5SL_item(node_ptr);
+
+ if ( next_entry_ptr == NULL ) {
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "next_entry_ptr == NULL 1 ?!?!");
+ }
+
+ HDassert( next_entry_ptr->magic == H5C2__H5C2_CACHE_ENTRY_T_MAGIC );
+ HDassert( next_entry_ptr->is_dirty );
+ HDassert( next_entry_ptr->in_slist );
+
+ }
+
+#if H5C2_DO_SANITY_CHECKS
+ /* Depending on circumstances, H5C2_flush_single_entry() will
+ * remove dirty entries from the slist as it flushes them.
+ * Thus for sanity checks we must make note of the initial
+ * slist length and size before we do any flushes.
+ */
+ initial_slist_len = cache_ptr->slist_len;
+ initial_slist_size = cache_ptr->slist_size;
+
+ /* There is also the possibility that entries will be
+ * dirtied, resized, and/or renamed as the result of
+ * calls to the flush callbacks. We use the slist_len_increase
+ * and slist_size_increase increase fields in struct H5C2_t
+ * to track these changes for purpose of sanity checking.
+ * To this end, we must zero these fields before we start
+ * the pass through the slist.
+ */
+ cache_ptr->slist_len_increase = 0;
+ cache_ptr->slist_size_increase = 0;
+
+ /* Finally, reset the actual_slist_len and actual_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;
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ while ( node_ptr != NULL )
+ {
+ entry_ptr = next_entry_ptr;
+
+ /* With the advent of the fractal heap, it is possible
+ * that the serialize 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 serialize 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.
+ */
+
+ if ( entry_ptr->magic != H5C2__H5C2_CACHE_ENTRY_T_MAGIC ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "entry_ptr->magic is invalid ?!?!");
+
+ } else if ( ( ! entry_ptr->is_dirty ) ||
+ ( ! entry_ptr->in_slist ) ) {
+
+ /* the s-list has been modified out from under us.
+ * break out of the loop.
+ */
+ break;
+ }
+
+ /* increment node pointer now, before we delete its target
+ * from the slist.
+ */
+ node_ptr = H5SL_next(node_ptr);
+
+ if ( node_ptr != NULL ) {
+
+ next_entry_ptr = (H5C2_cache_entry_t *)H5SL_item(node_ptr);
+
+ if ( next_entry_ptr == NULL ) {
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "next_entry_ptr == NULL 2 ?!?!");
+ }
+
+ HDassert( next_entry_ptr->magic ==
+ H5C2__H5C2_CACHE_ENTRY_T_MAGIC );
+ HDassert( next_entry_ptr->is_dirty );
+ HDassert( next_entry_ptr->in_slist );
+
+ } else {
+
+ next_entry_ptr = NULL;
+ }
+
+ /* Note that we now remove nodes from the slist as we flush
+ * the associated entries, instead of leaving them there
+ * until we are done, and then destroying all nodes in
+ * the slist.
+ *
+ * While this optimization used to be easy, with the possibility
+ * of new entries being added to the slist in the midst of the
+ * flush, we must keep the slist in cannonical form at all
+ * times.
+ */
+
+ HDassert( entry_ptr != NULL );
+ HDassert( entry_ptr->in_slist );
+
+#if H5C2_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 /* H5C2_DO_SANITY_CHECKS */
+
+ if ( entry_ptr->is_protected ) {
+
+ /* we have major problems -- but lets flush
+ * everything we can before we flag an error.
+ */
+ protected_entries++;
+
+ } else if ( entry_ptr->is_pinned ) {
+
+ /* Test to see if we are can flush the entry now.
+ * If we can, go ahead and flush, but don't tell
+ * H5C2_flush_single_entry() to destroy the entry
+ * as pinned entries can't be evicted.
+ */
+ if ( TRUE ) { /* When we get to multithreaded cache,
+ * we will need either locking code, and/or
+ * a test to see if the entry is in flushable
+ * condition here.
+ */
+
+ status = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ H5C2__NO_FLAGS_SET,
+ FALSE);
+ if ( status < 0 ) {
+
+ /* This shouldn't happen -- if it does, we are toast
+ * so just scream and die.
+ */
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "dirty pinned entry flush failed.")
+ }
+ }
+ } else {
+
+ status = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ (cooked_flags |
+ H5C2__FLUSH_INVALIDATE_FLAG),
+ TRUE);
+ if ( status < 0 ) {
+
+ /* This shouldn't happen -- if it does, we are toast so
+ * just scream and die.
+ */
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "dirty entry flush destroy failed.")
+ }
+ }
+
+ } /* end while loop scanning skip list */
+
+#if H5C2_DO_SANITY_CHECKS
+ /* It is possible that entries were added to the slist during
+ * the scan, either before or after scan pointer. The following
+ * asserts take this into account.
+ *
+ * Don't bother with the sanity checks if node_ptr != NULL, as
+ * in this case we broke out of the loop because it got changed
+ * out from under us.
+ */
+
+ if ( node_ptr == NULL ) {
+
+ HDassert( (actual_slist_len + cache_ptr->slist_len) ==
+ (initial_slist_len + cache_ptr->slist_len_increase) );
+ HDassert( (actual_slist_size + cache_ptr->slist_size) ==
+ (initial_slist_size + cache_ptr->slist_size_increase) );
+ }
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ /* Since we are doing a destroy, we must make a pass through
+ * the hash table and try to flush - destroy all entries that
+ * remain.
+ *
+ * It used to be that all entries remaining in the cache at
+ * this point had to be clean, but with the fractal heap mods
+ * this may not be the case. If so, we will flush entries out
+ * of increasing address order.
+ *
+ * Writes to disk are possible here.
+ */
+ for ( i = 0; i < H5C2__HASH_TABLE_LEN; i++ )
+ {
+ next_entry_ptr = cache_ptr->index[i];
+
+ while ( next_entry_ptr != NULL )
+ {
+ entry_ptr = next_entry_ptr;
+
+ HDassert( entry_ptr->magic == H5C2__H5C2_CACHE_ENTRY_T_MAGIC );
+
+ next_entry_ptr = entry_ptr->ht_next;
+
+ HDassert ( ( next_entry_ptr == NULL ) ||
+ ( next_entry_ptr->magic ==
+ H5C2__H5C2_CACHE_ENTRY_T_MAGIC ) );
+
+ if ( entry_ptr->is_protected ) {
+
+ /* we have major problems -- but lets flush and destroy
+ * everything we can before we flag an error.
+ */
+ protected_entries++;
+
+ if ( ! entry_ptr->in_slist ) {
+
+ HDassert( !(entry_ptr->is_dirty) );
+ }
+ } else if ( ! ( entry_ptr->is_pinned ) ) {
+
+ status =
+ H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ (cooked_flags |
+ H5C2__FLUSH_INVALIDATE_FLAG),
+ TRUE);
+ if ( status < 0 ) {
+
+ /* This shouldn't happen -- if it does, we are toast so
+ * just scream and die.
+ */
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "Entry flush destroy failed.")
+ }
+ }
+ /* We can't do anything if the entry is pinned. The
+ * hope is that the entry will be unpinned as the
+ * result of destroys of entries that reference it.
+ *
+ * We detect this by noting the change in the number
+ * of pinned entries from pass to pass. If it stops
+ * shrinking before it hits zero, we scream and die.
+ */
+
+ /* 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 *nexte_entry_ptr was flushed or evicted.
+ *
+ * Test to see if this happened here, and set next_entry_ptr
+ * to NULL if it did. Note that if this test is triggred,
+ * we are accessing a deallocated piece of dynamically
+ * allocated memory, so we just scream and die.
+ */
+ if ( ( next_entry_ptr != NULL ) &&
+ ( next_entry_ptr->magic !=
+ H5C2__H5C2_CACHE_ENTRY_T_MAGIC ) ) {
+
+ /* Something horrible has happened to
+ * *next_entry_ptr -- scream and die.
+ */
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "next_entry_ptr->magic is invalid?!?!?.")
+ }
+ } /* end while loop scanning hash table bin */
+ } /* end for loop scanning hash table */
+
+ old_pel_len = cur_pel_len;
+ cur_pel_len = cache_ptr->pel_len;
+
+ if ( ( cur_pel_len > 0 ) && ( cur_pel_len >= old_pel_len ) ) {
+
+ /* The number of pinned entries is positive, and it is not
+ * declining. Scream and die.
+ */
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "Can't unpin all pinned entries 1.")
+
+ } else if ( ( cur_pel_len == 0 ) && ( old_pel_len == 0 ) ) {
+
+ /* increment the pass count */
+ passes++;
+ }
+
+ if ( passes >= H5C2__MAX_PASSES_ON_FLUSH ) {
+
+ /* we have exceeded the maximum number of passes through the
+ * cache to flush and destroy all entries. Scream and die.
+ */
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "Maximum passes on flush exceeded.")
+ }
+
+ if ( cache_ptr->index_len <= 0 ) {
+
+ done = TRUE;
+ HDassert( cache_ptr->index_size == 0 );
+ HDassert( cache_ptr->slist_len == 0 );
+ HDassert( cache_ptr->slist_size == 0 );
+ HDassert( cache_ptr->pel_len == 0 );
+ HDassert( cache_ptr->pel_size == 0 );
+ HDassert( cache_ptr->pl_len == 0 );
+ HDassert( cache_ptr->pl_size == 0 );
+ HDassert( cache_ptr->LRU_list_len == 0 );
+ HDassert( cache_ptr->LRU_list_size == 0 );
+ }
+
+ } /* main while loop */
+
+ HDassert( protected_entries <= cache_ptr->pl_len );
+
+ if ( protected_entries > 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "Cache has protected entries.")
+
+ } else if ( cur_pel_len > 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "Can't unpin all pinned entries 2.")
+
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_flush_invalidate_cache() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_flush_single_entry
+ *
+ * Purpose: Flush or clear (and evict if requested) the cache entry
+ * with the specified address and type. If the type is NULL,
+ * any unprotected entry at the specified address will be
+ * flushed (and possibly evicted).
+ *
+ * Attempts to flush a protected entry will result in an
+ * error.
+ *
+ * *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 H5C2__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.
+ *
+ * If the caller knows the address of the TBBT node at
+ * which the target entry resides, it can avoid a lookup
+ * by supplying that address in the tgt_node_ptr parameter.
+ * If this parameter is NULL, the function will do a TBBT
+ * search for the entry instead.
+ *
+ * The function does nothing silently if there is no entry
+ * at the supplied address, or if the entry found has the
+ * wrong type.
+ *
+ * Return: Non-negative on success/Negative on failure or if there was
+ * an attempt to flush a protected item.
+ *
+ * Programmer: John Mainzer, 5/5/04
+ *
+ * Modifications:
+ *
+ * JRM -- 7/21/04
+ * Updated function for the addition of the hash table.
+ *
+ * QAK -- 11/26/04
+ * Updated function for the switch from TBBTs to skip lists.
+ *
+ * JRM -- 1/6/05
+ * Updated function to reset the flush_marker field.
+ * Also replace references to H5F_FLUSH_INVALIDATE and
+ * H5F_FLUSH_CLEAR_ONLY with references to
+ * H5C2__FLUSH_INVALIDATE_FLAG and H5C2__FLUSH_CLEAR_ONLY_FLAG
+ * respectively.
+ *
+ * JRM -- 6/24/05
+ * Added code to remove dirty entries from the slist after
+ * they have been flushed. Also added a sanity check that
+ * will scream if we attempt a write when writes are
+ * completely disabled.
+ *
+ * JRM -- 7/5/05
+ * Added code to call the new log_flush callback whenever
+ * a dirty entry is written to disk. Note that the callback
+ * is not called if the H5C2__FLUSH_CLEAR_ONLY_FLAG is set,
+ * as there is no write to file in this case.
+ *
+ * JRM -- 8/21/06
+ * Added code maintaining the flush_in_progress and
+ * destroy_in_progress fields in H5C2_cache_entry_t.
+ *
+ * Also added flush_flags parameter to the call to
+ * type_ptr->flush() so that the flush routine can report
+ * whether the entry has been resized or renamed. Added
+ * code using the flush_flags variable to detect the case
+ * in which the target entry is resized during flush, and
+ * update the caches data structures accordingly.
+ *
+ *
+ * JRM -- 3/29/07
+ * Added sanity checks on the new is_read_only and
+ * ro_ref_count fields.
+ *
+ * JRM -- 6/25/07
+ * Rewrite of function to use the new metadata cache callback
+ * functions. These functions move all metadata file I/O into
+ * the cache proper, which is necessary for metadata journaling.
+ *
+ * To date, the functions of the H5C2__FLUSH_INVALIDATE_FLAG
+ * and H5C2__FLUSH_CLEAR_ONLY_FLAG have not been documented
+ * in H5C2, as these flags were just passed through to the
+ * client callbacks. As much of the callback functionality
+ * is now in the cache, the function of these flags should
+ * be documented explicitly here in H5C2.
+ *
+ * If the H5C2__FLUSH_INVALIDATE_FLAG is set, the entry is to
+ * be written to disk if dirty, and then evicted from the
+ * cache and discarded. As an optimization, the destroyed
+ * is deleted from the slist only on request.
+ *
+ * If the H5C2__FLUSH_CLEAR_ONLY_FLAG is set, the entry is
+ * to be marked clean if it is dirty. Under no circumstances
+ * will it be written to disk.
+ *
+ * If both the H5C2__FLUSH_INVALIDATE_FLAG and the
+ * H5C2__FLUSH_CLEAR_ONLY_FLAG are set, the entry is marked
+ * clean and then evicted from the cache without writing
+ * to disk. If dirty, the entry is removed from the slist
+ * or not as requested.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static herr_t
+H5C2_flush_single_entry(H5F_t * f,
+ hid_t dxpl_id,
+ H5C2_t * cache_ptr,
+ const H5C2_class_t * type_ptr,
+ haddr_t addr,
+ unsigned flags,
+ hbool_t del_entry_from_slist_on_destroy)
+{
+ /* const char * fcn_name = "H5C2_flush_single_entry()"; */
+ hbool_t destroy;
+ hbool_t clear_only;
+ hbool_t was_dirty;
+ herr_t ret_value = SUCCEED; /* Return value */
+ herr_t status;
+ int type_id;
+ unsigned serialize_flags = 0;
+ haddr_t new_addr;
+ size_t new_len;
+ void * new_image_ptr;
+ H5C2_cache_entry_t * entry_ptr = NULL;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2_flush_single_entry)
+
+ HDassert( f );
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( type_ptr );
+ HDassert( H5F_addr_defined(addr) );
+
+ destroy = ( (flags & H5C2__FLUSH_INVALIDATE_FLAG) != 0 );
+ clear_only = ( (flags & H5C2__FLUSH_CLEAR_ONLY_FLAG) != 0);
+
+ /* attempt to find the target entry in the hash table */
+ H5C2__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL)
+
+#if H5C2_DO_SANITY_CHECKS
+ if ( entry_ptr != NULL ) {
+
+ HDassert( ! ( ( destroy ) && ( entry_ptr->is_pinned ) ) );
+
+ if ( entry_ptr->in_slist ) {
+
+ if ( ( ( entry_ptr->flush_marker ) && ( ! entry_ptr->is_dirty ) )
+ ||
+ ( entry_ptr->addr != addr ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "entry in slist failed sanity checks.")
+ }
+ } else {
+
+ if ( ( entry_ptr->is_dirty ) ||
+ ( entry_ptr->flush_marker ) ||
+ ( entry_ptr->addr != addr ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "entry failed sanity checks.")
+ }
+ }
+ }
+#if 1
+ /* this should be useful for debugging from time to time.
+ * lets leave it in for now. -- JRM 12/15/04
+ */
+ else {
+ HDfprintf(stdout,
+ "H5C2_flush_single_entry(): non-existant entry. addr = 0x%lx\n",
+ (long)addr);
+ HDfflush(stdout);
+ }
+#endif
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ if ( ( entry_ptr != NULL ) && ( 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 ) ) )
+ {
+ /* we have work to do */
+
+ /* 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;
+
+#ifdef H5_HAVE_PARALLEL
+#ifndef NDEBUG
+
+ /* If MPI based VFD is used, do special parallel I/O sanity checks.
+ * Note that we only do these sanity checks when the clear_only flag
+ * is not set, and the entry to be flushed is dirty. Don't bother
+ * otherwise as no file I/O can result.
+ */
+ if ( ( ! clear_only ) &&
+ ( entry_ptr->is_dirty ) &&
+ ( IS_H5FD_MPI(f) ) ) {
+
+ H5P_genplist_t *dxpl; /* Dataset transfer property list */
+ H5FD_mpio_xfer_t xfer_mode; /* I/O xfer mode property value */
+
+ /* Get the dataset transfer property list */
+ if ( NULL == (dxpl = H5I_object(dxpl_id)) ) {
+
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, \
+ "not a dataset creation property list")
+ }
+
+ /* Get the transfer mode property */
+ if( H5P_get(dxpl, H5D_XFER_IO_XFER_MODE_NAME, &xfer_mode) < 0 ) {
+
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, \
+ "can't retrieve xfer mode")
+ }
+
+ /* Sanity check transfer mode */
+ /* I'm surprised that this sanity check is working at
+ * present -- will need to look into it at some point.
+ *
+ * JRM -- 7/7/07
+ */
+
+ HDassert( xfer_mode == H5FD_MPIO_COLLECTIVE );
+ }
+
+#endif /* NDEBUG */
+#endif /* H5_HAVE_PARALLEL */
+
+ was_dirty = entry_ptr->is_dirty;
+ type_id = entry_ptr->type->id;
+
+ entry_ptr->flush_marker = FALSE;
+
+ if ( clear_only ) {
+ H5C2__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr)
+ } else {
+ H5C2__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr)
+ }
+
+ if ( destroy ) {
+ H5C2__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr)
+ }
+
+ /* 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.
+ *
+ * Note that it is possible that the entry will be renamed during
+ * its call to flush. This will upset H5C2_rename_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.
+ */
+ if ( destroy ) {
+
+ entry_ptr->destroy_in_progress = TRUE;
+
+ H5C2__DELETE_FROM_INDEX(cache_ptr, entry_ptr)
+
+ if ( ( entry_ptr->in_slist ) &&
+ ( del_entry_from_slist_on_destroy ) ) {
+
+ H5C2__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr)
+ }
+ }
+
+ /* Update the replacement policy for the flush or eviction. */
+ if ( destroy ) { /* AKA eviction */
+
+#if 0 /* JRM */
+ /* This test code may come in handy -- lets keep it for a while.
+ *
+ * Note that it will cause spurious errors in the serial case
+ * unless we are maintaining the clean and dirty LRU lists.
+ */
+ {
+ if ( entry_ptr->is_dirty )
+ {
+ if ( cache_ptr->dLRU_head_ptr == NULL )
+ HDfprintf(stdout,
+ "%s: cache_ptr->dLRU_head_ptr == NULL.\n",
+ fcn_name);
+
+ if ( cache_ptr->dLRU_tail_ptr == NULL )
+ HDfprintf(stdout,
+ "%s: cache_ptr->dLRU_tail_ptr == NULL.\n",
+ fcn_name);
+
+ if ( cache_ptr->dLRU_list_len <= 0 )
+ HDfprintf(stdout,
+ "%s: cache_ptr->dLRU_list_len <= 0.\n",
+ fcn_name);
+
+ if ( cache_ptr->dLRU_list_size <= 0 )
+ HDfprintf(stdout,
+ "%s: cache_ptr->dLRU_list_size <= 0.\n",
+ fcn_name);
+
+ if ( cache_ptr->dLRU_list_size < entry_ptr->size )
+ HDfprintf(stdout,
+ "%s: cache_ptr->dLRU_list_size < entry_ptr->size.\n",
+ fcn_name);
+
+ if ( ( (cache_ptr->dLRU_list_size) == entry_ptr->size ) &&
+ ( ! ( (cache_ptr->dLRU_list_len) == 1 ) ) )
+ HDfprintf(stdout,
+ "%s: dLRU_list_size == size && dLRU_list_len != 1\n",
+ fcn_name);
+
+ if ( ( entry_ptr->aux_prev == NULL ) &&
+ ( cache_ptr->dLRU_head_ptr != entry_ptr ) )
+ HDfprintf(stdout,
+ "%s: entry_ptr->aux_prev == NULL && dLRU_head_ptr != entry_ptr\n",
+ fcn_name);
+
+ if ( ( entry_ptr->aux_next == NULL ) &&
+ ( cache_ptr->dLRU_tail_ptr != entry_ptr ) )
+ HDfprintf(stdout,
+ "%s: entry_ptr->aux_next == NULL && dLRU_tail_ptr != entry_ptr\n",
+ fcn_name);
+
+ if ( ( cache_ptr->dLRU_list_len == 1 ) &&
+ ( ! ( ( cache_ptr->dLRU_head_ptr == entry_ptr ) &&
+ ( cache_ptr->dLRU_tail_ptr == entry_ptr ) &&
+ ( entry_ptr->aux_next == NULL ) &&
+ ( entry_ptr->aux_prev == NULL ) &&
+ ( cache_ptr->dLRU_list_size == entry_ptr->size )
+ )
+ )
+ )
+ {
+ HDfprintf(stdout,
+ "%s: single entry dlru sanity check fails\n",
+ fcn_name);
+ }
+
+ }
+ else
+ {
+ if ( cache_ptr->cLRU_head_ptr == NULL )
+ HDfprintf(stdout,
+ "%s: cache_ptr->cLRU_head_ptr == NULL.\n",
+ fcn_name);
+
+ if ( cache_ptr->cLRU_tail_ptr == NULL )
+ HDfprintf(stdout,
+ "%s: cache_ptr->cLRU_tail_ptr == NULL.\n",
+ fcn_name);
+
+ if ( cache_ptr->cLRU_list_len <= 0 )
+ HDfprintf(stdout,
+ "%s: cache_ptr->cLRU_list_len <= 0.\n",
+ fcn_name);
+
+ if ( cache_ptr->cLRU_list_size <= 0 )
+ HDfprintf(stdout,
+ "%s: cache_ptr->cLRU_list_size <= 0.\n",
+ fcn_name);
+
+ if ( cache_ptr->cLRU_list_size < entry_ptr->size )
+ HDfprintf(stdout,
+ "%s: cache_ptr->cLRU_list_size < entry_ptr->size.\n",
+ fcn_name);
+
+ if ( ( (cache_ptr->cLRU_list_size) == entry_ptr->size ) &&
+ ( ! ( (cache_ptr->cLRU_list_len) == 1 ) ) )
+ HDfprintf(stdout,
+ "%s: cLRU_list_size == size && cLRU_list_len != 1\n",
+ fcn_name);
+
+ if ( ( entry_ptr->aux_prev == NULL ) &&
+ ( cache_ptr->cLRU_head_ptr != entry_ptr ) )
+ HDfprintf(stdout, "%s: entry_ptr->aux_prev == NULL && cLRU_head_ptr != entry_ptr\n", fcn_name);
+
+ if ( ( entry_ptr->aux_next == NULL ) &&
+ ( cache_ptr->cLRU_tail_ptr != entry_ptr ) )
+ HDfprintf(stdout, "%s: entry_ptr->aux_next == NULL && cLRU_tail_ptr != entry_ptr\n", fcn_name);
+
+ if ( ( cache_ptr->cLRU_list_len == 1 ) &&
+ ( ! ( ( cache_ptr->cLRU_head_ptr == entry_ptr ) &&
+ ( cache_ptr->cLRU_tail_ptr == entry_ptr ) &&
+ ( entry_ptr->aux_next == NULL ) &&
+ ( entry_ptr->aux_prev == NULL ) &&
+ ( cache_ptr->cLRU_list_size == entry_ptr->size )
+ )
+ )
+ )
+ {
+ HDfprintf(stdout,
+ "%s: single entry clru sanity check fails\n",
+ fcn_name);
+ }
+ }
+ }
+#endif /* JRM */
+
+ H5C2__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, FAIL)
+
+ } else {
+ /* 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
+ */
+
+ H5C2__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, FAIL)
+
+ if ( entry_ptr->in_slist )
+ {
+ H5C2__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr)
+ }
+ }
+
+ /* Clear the dirty flag only, if requested */
+ if ( clear_only )
+ {
+ if ( entry_ptr->is_dirty )
+ {
+#ifndef NDEBUG
+ /* only call the clear_dirty_bits callback if debugging
+ * is enabled.
+ */
+ if ( entry_ptr->type->clear_dirty_bits(addr,
+ entry_ptr->size,
+ (void *)entry_ptr)
+ != SUCCEED )
+ {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "clear_dirty_bits() failed.")
+ }
+#endif /* NDEBUG */
+ }
+ entry_ptr->is_dirty = FALSE;
+
+ }
+ else if ( entry_ptr->is_dirty )
+ {
+ /* 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.
+ *
+ * If the entry is clean, we do nothing at this point.
+ *
+ * If the serialize function changes the size or location
+ * of the entry, and we are not doing a flush destroy, we
+ * will have to touch up the cache to account for the
+ * change(s).
+ */
+
+#if H5C2_DO_SANITY_CHECKS
+ if ( ( cache_ptr->check_write_permitted == NULL ) &&
+ ( ! (cache_ptr->write_permitted) ) ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Write when writes are always forbidden!?!?!")
+ }
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ if ( entry_ptr->image_ptr == NULL )
+ {
+ entry_ptr->image_ptr = H5MM_malloc(entry_ptr->size);
+
+ if ( entry_ptr->image_ptr == NULL )
+ {
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "memory allocation failed for on disk image buffer.")
+ }
+ }
+
+ if ( entry_ptr->type->serialize(entry_ptr->addr,
+ entry_ptr->size,
+ entry_ptr->image_ptr,
+ (void *)entry_ptr,
+ &serialize_flags,
+ &new_addr,
+ &new_len,
+ &new_image_ptr) != SUCCEED )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unable to serialize entry")
+ }
+
+ if ( serialize_flags != 0 )
+ {
+ if ( destroy )
+ {
+ /* We have already removed the entry from the
+ * cache's data structures, so no need to update
+ * them for the re-size and/or rename. All we need
+ * to do is update the cache entry so we will have
+ * the correct values when we actually write the
+ * image of the entry to disk.
+ *
+ * Note that if the serialize function changes the
+ * size of the disk image of the entry, it must
+ * deallocate the old image, and allocate a new.
+ */
+
+ switch ( serialize_flags )
+ {
+ case H5C2__SERIALIZE_RESIZED_FLAG:
+ H5C2__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr,
+ entry_ptr,
+ new_len)
+ entry_ptr->size = new_len;
+ entry_ptr->image_ptr = new_image_ptr;
+ break;
+
+ case (H5C2__SERIALIZE_RESIZED_FLAG |
+ H5C2__SERIALIZE_RENAMED_FLAG):
+ H5C2__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr,
+ entry_ptr,
+ new_len)
+ entry_ptr->addr = new_addr;
+ entry_ptr->size = new_len;
+ entry_ptr->image_ptr = new_image_ptr;
+ break;
+
+ default:
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unexpected serialize flag(s)")
+ break;
+ }
+ }
+ else
+ {
+ /* The entry is not being destroyed, and thus has not
+ * been removed from the cache's data structures.
+ *
+ * Thus, in addition to updating the entry for the
+ * re-size and/or rename, we must also update the
+ * cache data structures.
+ */
+
+ switch ( serialize_flags )
+ {
+ case H5C2__SERIALIZE_RESIZED_FLAG:
+ H5C2__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr,
+ entry_ptr,
+ new_len)
+ /* The replacement policy code thinks the
+ * entry is already clean, so modify is_dirty
+ * to meet this expectation.
+ */
+ entry_ptr->is_dirty = FALSE;
+
+ /* update the hash table for the size change*/
+ H5C2__UPDATE_INDEX_FOR_SIZE_CHANGE((cache_ptr), \
+ (entry_ptr->size),\
+ (new_len));
+
+ /* 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.
+ */
+ H5C2__UPDATE_RP_FOR_SIZE_CHANGE(cache_ptr, \
+ entry_ptr, \
+ new_len);
+
+ /* The entry can't be in the slist, so no need
+ * to update the slist for the size change.
+ */
+
+ /* finally, set is_dirty to TRUE again, and
+ * update the size and image_ptr.
+ */
+ entry_ptr->is_dirty = TRUE;
+ entry_ptr->size = new_len;
+ entry_ptr->image_ptr = new_image_ptr;
+ break;
+
+ case (H5C2__SERIALIZE_RESIZED_FLAG |
+ H5C2__SERIALIZE_RENAMED_FLAG):
+ H5C2__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr,
+ entry_ptr,
+ new_len)
+ /* The replacement policy code thinks the
+ * entry is already clean, so modify is_dirty
+ * to meet this expectation.
+ */
+ entry_ptr->is_dirty = FALSE;
+
+ /* first update the hash table for the rename */
+ H5C2__DELETE_FROM_INDEX(cache_ptr, entry_ptr)
+ entry_ptr->addr = new_addr;
+ H5C2__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL)
+
+ /* update the hash table for the size change */
+ H5C2__UPDATE_INDEX_FOR_SIZE_CHANGE((cache_ptr), \
+ (entry_ptr->size),\
+ (new_len));
+
+ /* 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.
+ */
+ H5C2__UPDATE_RP_FOR_SIZE_CHANGE(cache_ptr, \
+ entry_ptr, \
+ new_len);
+
+ /* The entry can't be in the slist, so no need
+ * to update the slist for the size change.
+ */
+
+ /* finally, set is_dirty to TRUE again, and
+ * update the size and image_ptr.
+ */
+ entry_ptr->is_dirty = TRUE;
+
+ entry_ptr->size = new_len;
+ entry_ptr->image_ptr = new_image_ptr;
+ break;
+
+ default:
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unexpected serialize flag(s)")
+ break;
+ }
+ }
+ }
+
+ /* now write the image to disk */
+ if ( H5F_block_write(f, type_ptr->mem_type, entry_ptr->addr,
+ entry_ptr->size, dxpl_id,
+ entry_ptr->image_ptr) < 0 )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "Can't write image to file.")
+ }
+
+#ifdef H5_HAVE_PARALLEL
+ if ( serialize_flags != 0 ) {
+
+ /* In the parallel case, resizes and renames 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 rename themselves during a flush
+ * in the parallel case, it will not detect an
+ * entry that dirties, resizes, and/or renames
+ * 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/rename in serialize occured in parallel case.")
+
+ }
+ }
+#endif /* H5_HAVE_PARALLEL */
+
+ entry_ptr->is_dirty = FALSE;
+ }
+
+ if ( destroy ) /* time to discard the entry */
+ {
+ /* 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);
+
+ if ( entry_ptr->image_ptr != NULL ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "*image_ptr free failed.")
+ }
+ }
+
+ /* 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 = H5C2__H5C2_CACHE_ENTRY_T_BAD_MAGIC;
+
+ if ( type_ptr->free_icr(entry_ptr->addr, entry_ptr->size,
+ (void *)entry_ptr) != SUCCEED )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "free_icr callback failed.")
+ }
+ }
+ else /* just flushing or clearing */
+ {
+ entry_ptr->flush_in_progress = FALSE;
+ }
+
+ if ( cache_ptr->log_flush ) {
+
+ /* JRM */ /* may want to update this */
+ status = (cache_ptr->log_flush)(cache_ptr, addr, was_dirty,
+ flags, type_id);
+
+ if ( status < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "log_flush callback failed.")
+ }
+ }
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_flush_single_entry() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_load_entry
+ *
+ * Purpose: Attempt to load the entry at the specified disk address
+ * and with the specified type into memory. If successful.
+ * return the in memory address of the entry. Return NULL
+ * on failure.
+ *
+ * Note that this function simply loads the entry into
+ * core. It does not insert it into the cache.
+ *
+ * Return: Non-NULL on success / NULL on failure.
+ *
+ * Programmer: John Mainzer, 5/18/04
+ *
+ * Modifications:
+ *
+ * JRM - 7/21/04
+ * Updated function for the addition of the hash table.
+ *
+ * JRM - 6/23/06
+ * Deleted assertion that verified that a newly loaded
+ * entry is clean. Due to a bug fix, this need not be
+ * the case, as our code will attempt to repair errors
+ * on load.
+ *
+ * JRM - 8/21/06
+ * Added initialization for the new flush_in_progress and
+ * destroy in progress fields.
+ *
+ * JRM - 3/29/07
+ * Added initialization for the new is_read_only and
+ * ro_ref_count fields.
+ *
+ * JRM - 6/25/07
+ * Reworked function to use the new client callback
+ * functions that are needed to implement metadata
+ * journaling. Removed skip_file_checks parameter.
+ *
+ * JRM -- 10/12/07
+ * Added initialization for the new magic field.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static void *
+H5C2_load_entry(H5F_t * f,
+ hid_t dxpl_id,
+ const H5C2_class_t * type,
+ haddr_t addr,
+ size_t len,
+ hbool_t chk_len,
+ const void * udata_ptr)
+{
+ /* const char * fcn_name = "H5C2_load_entry()"; */
+ hbool_t dirty = FALSE;
+ haddr_t abs_eoa; /* Absolute end of file address */
+ haddr_t rel_eoa; /* Relative end of file address */
+ void * image_ptr = NULL;
+ void * thing = NULL;
+ void * ret_value = NULL;
+ H5C2_cache_entry_t * entry_ptr = NULL;
+ size_t new_len;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2_load_entry)
+
+ HDassert( f );
+ HDassert( type );
+ HDassert( H5F_addr_defined(addr) );
+ HDassert( len > 0 );
+ HDassert( type->deserialize );
+ HDassert( ( ! chk_len ) || ( type->image_len ) );
+
+ /* Make certain we don't speculatively read off the end of the file */
+ if ( HADDR_UNDEF == (abs_eoa = H5F_get_eoa(f)) )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, \
+ "unable to determine file size")
+ }
+
+ /* Adjust absolute EOA address to relative EOA address */
+ rel_eoa = abs_eoa - H5F_get_base_addr(f);
+
+ HDassert( rel_eoa > addr );
+
+ if ( (rel_eoa - addr) < (haddr_t)len )
+ {
+ /* either the client is speculatively reading beyond the end of
+ * file, or there is a major screw up. Adjust the len in the
+ * former case, and scream and die in the latter
+ */
+ if ( chk_len ) /* i.e. possible speculative read beyond eof */
+ {
+ /* Quincey: Did I use this correctly? In the example I
+ * stole it from, the from type was hsize_t even though
+ * the source was a haddr_t. I changed the from to match
+ * the source. Is this as it should be?
+ */
+ /* JRM */
+ H5_ASSIGN_OVERFLOW(len, (rel_eoa - addr), \
+ /* from: */ haddr_t, /* to: */ size_t);
+ HDassert( len > 0 );
+ }
+ else
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, \
+ "tried to read beyond eof")
+
+ }
+ }
+
+ image_ptr = H5MM_malloc(len);
+
+ if ( image_ptr == NULL )
+ {
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, \
+ "memory allocation failed for on disk image buffer.")
+ }
+
+ if ( H5F_block_read(f, type->mem_type, addr, len, dxpl_id, image_ptr) < 0 )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "Can't read image")
+ }
+
+ thing = type->deserialize(addr, len, image_ptr, udata_ptr, &dirty);
+
+ if ( thing == NULL )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "Can't deserialize image")
+ }
+
+ if ( chk_len )
+ {
+ if ( type->image_len(thing, &new_len) != SUCCEED )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "image_len() failed")
+ }
+ else if ( new_len > len )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "new_len > len")
+ }
+ else if ( new_len <= 0 )
+ {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "new_len <= 0")
+ }
+ else if ( new_len < len )
+ {
+ thing = H5MM_realloc(thing, new_len);
+
+ if ( thing == NULL )
+ {
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, \
+ "thing null after H5MM_realloc().")
+ }
+ else
+ {
+ len = new_len;
+ }
+ }
+ }
+
+ entry_ptr = (H5C2_cache_entry_t *)thing;
+
+ /* In general, an entry should be clean just after it is loaded.
+ *
+ * However, when this code is used in the metadata cache, it is
+ * possible that object headers will be dirty at this point, as
+ * the deserialize function will alter object headers if necessary to
+ * fix an old bug.
+ *
+ * In the following assert:
+ *
+ * HDassert( ( dirty == FALSE ) || ( type->id == 4 ) );
+ *
+ * note that type id 4 is associated with object headers in the metadata
+ * cache.
+ *
+ * When we get to using H5C2 for other purposes, we may wish to
+ * tighten up the assert so that the loophole only applies to the
+ * metadata cache.
+ */
+
+ HDassert( ( dirty == FALSE ) || ( type->id == 4 ) );
+ HDassert( entry_ptr->size < H5C2_MAX_ENTRY_SIZE );
+
+ entry_ptr->magic = H5C2__H5C2_CACHE_ENTRY_T_MAGIC;
+ entry_ptr->addr = addr;
+ entry_ptr->size = len;
+ entry_ptr->image_ptr = image_ptr;
+ entry_ptr->type = type;
+ entry_ptr->is_dirty = dirty;
+ entry_ptr->dirtied = FALSE;
+ entry_ptr->is_protected = FALSE;
+ entry_ptr->is_read_only = FALSE;
+ entry_ptr->ro_ref_count = 0;
+ entry_ptr->is_pinned = FALSE;
+ entry_ptr->in_slist = FALSE;
+ entry_ptr->flush_marker = FALSE;
+#ifdef H5_HAVE_PARALLEL
+ entry_ptr->clear_on_unprotect = FALSE;
+#endif /* H5_HAVE_PARALLEL */
+ entry_ptr->flush_in_progress = FALSE;
+ entry_ptr->destroy_in_progress = FALSE;
+
+ entry_ptr->ht_next = NULL;
+ entry_ptr->ht_prev = NULL;
+
+ entry_ptr->next = NULL;
+ entry_ptr->prev = NULL;
+
+ entry_ptr->aux_next = NULL;
+ entry_ptr->aux_prev = NULL;
+
+ H5C2__RESET_CACHE_ENTRY_STATS(entry_ptr);
+
+ ret_value = thing;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_load_entry() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_make_space_in_cache
+ *
+ * Purpose: Attempt to evict cache entries until the index_size
+ * is at least needed_space below max_cache_size.
+ *
+ * In passing, also attempt to bring cLRU_list_size to a
+ * value greater than min_clean_size.
+ *
+ * Depending on circumstances, both of these goals may
+ * be impossible, as in parallel mode, we must avoid generating
+ * a write as part of a read (to avoid deadlock in collective
+ * I/O), and in all cases, it is possible (though hopefully
+ * highly unlikely) that the protected list may exceed the
+ * maximum size of the cache.
+ *
+ * Thus the function simply does its best, returning success
+ * unless an error is encountered.
+ *
+ * The primary_dxpl_id and secondary_dxpl_id parameters
+ * specify the dxpl_ids used on the first write occasioned
+ * by the call (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.
+ *
+ * Observe that this function cannot occasion a read.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 5/14/04
+ *
+ * Modifications:
+ *
+ * JRM --7/21/04
+ * Minor modifications in support of the addition of a hash
+ * table to facilitate lookups.
+ *
+ * JRM -- 11/22/04
+ * Added the first_flush_ptr parameter, which replaces the
+ * old first_flush local variable. This allows the function
+ * to coordinate on the first flush issue with other functions.
+ *
+ * JRM -- 12/13/04
+ * Added code to skip over epoch markers if present.
+ *
+ * JRM -- 1/3/06
+ * Modified function to work correctly when the the cache
+ * is not full. This case occurs when we need to flush to
+ * min clean size before the cache has filled.
+ *
+ * JRM -- 3/29/07
+ * Added sanity checks using the new is_read_only and
+ * ro_ref_count fields.
+ *
+ * JRM -- 8/24/07
+ * Reworked parameter list and code for the removal of the
+ * secondary dxpl id, and the decision to store the file
+ * pointer f in *cache_ptr.
+ *
+ * JRM -- 10/12/07
+ * Added code to detect the case in which the LRU list has
+ * been modified by a serialize callback, and cause the
+ * function to re-start its scan at the tail of the LRU.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static herr_t
+H5C2_make_space_in_cache(hid_t dxpl_id,
+ H5C2_t * cache_ptr,
+ size_t space_needed,
+ hbool_t write_permitted)
+{
+ /* const char * fcn_name = "H5C2_make_space_in_cache()"; */
+ herr_t ret_value = SUCCEED; /* Return value */
+ herr_t result;
+ int32_t entries_examined = 0;
+ int32_t initial_list_len;
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+ size_t empty_space;
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+ hbool_t prev_is_dirty = FALSE;
+ H5C2_cache_entry_t * entry_ptr;
+ H5C2_cache_entry_t * prev_ptr;
+ H5C2_cache_entry_t * next_ptr;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2_make_space_in_cache)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ if ( write_permitted ) {
+
+ initial_list_len = cache_ptr->LRU_list_len;
+ entry_ptr = cache_ptr->LRU_tail_ptr;
+
+ while ( ( (cache_ptr->index_size + space_needed)
+ >
+ cache_ptr->max_cache_size
+ )
+ &&
+ ( entries_examined <= (2 * initial_list_len) )
+ &&
+ ( entry_ptr != NULL )
+ )
+ {
+ HDassert( ! (entry_ptr->is_protected) );
+ HDassert( ! (entry_ptr->is_read_only) );
+ HDassert( (entry_ptr->ro_ref_count) == 0 );
+
+ next_ptr = entry_ptr->next;
+ prev_ptr = entry_ptr->prev;
+
+ if ( prev_ptr != NULL ) {
+
+ prev_is_dirty = prev_ptr->is_dirty;
+ }
+
+ if ( (entry_ptr->type)->id != H5C2__EPOCH_MARKER_TYPE ) {
+
+ if ( entry_ptr->is_dirty ) {
+
+ result = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ H5C2__NO_FLAGS_SET,
+ FALSE);
+ } else {
+
+ result =
+ H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ H5C2__FLUSH_INVALIDATE_FLAG,
+ TRUE);
+ }
+ } else {
+
+ /* Skip epoch markers. Set result to SUCCEED to avoid
+ * triggering the error code below.
+ */
+ result = SUCCEED;
+ }
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unable to flush entry 1")
+ }
+
+ if ( prev_ptr != NULL ) {
+
+ if ( prev_ptr->magic != H5C2__H5C2_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")
+
+ } else if ( ( 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.
+ */
+ entry_ptr = cache_ptr->LRU_tail_ptr;
+
+ } else {
+
+ entry_ptr = prev_ptr;
+
+ }
+ } else {
+
+ entry_ptr = NULL;
+
+ }
+
+ entries_examined++;
+
+ }
+
+#if H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+ initial_list_len = cache_ptr->dLRU_list_len;
+ entry_ptr = cache_ptr->dLRU_tail_ptr;
+
+ if ( cache_ptr->index_size < cache_ptr->max_cache_size ) {
+
+ empty_space = cache_ptr->max_cache_size - cache_ptr->index_size;
+
+ } else {
+
+ empty_space = 0;
+ }
+
+ while ( ( (cache_ptr->cLRU_list_size + empty_space)
+ < cache_ptr->min_clean_size ) &&
+ ( entries_examined <= initial_list_len ) &&
+ ( entry_ptr != NULL )
+ )
+ {
+ HDassert( ! (entry_ptr->is_protected) );
+ HDassert( ! (entry_ptr->is_read_only) );
+ HDassert( (entry_ptr->ro_ref_count) == 0 );
+ HDassert( entry_ptr->is_dirty );
+ HDassert( entry_ptr->in_slist );
+
+ prev_ptr = entry_ptr->aux_prev;
+
+ next_ptr = entry_ptr->aux_next;
+
+ if ( prev_ptr != NULL ) {
+
+ HDassert( prev_ptr->is_dirty );
+ }
+
+ result = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ H5C2__NO_FLAGS_SET,
+ FALSE);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unable to flush entry 2")
+ }
+
+ if ( prev_ptr != NULL ) {
+
+ if (prev_ptr->magic != H5C2__H5C2_CACHE_ENTRY_T_MAGIC) {
+
+ /* something horrible has happened to *prev_ptr --
+ * scream and die.
+ */
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "*prev_ptr corrupt 2")
+
+ } else if ( ( ! ( prev_ptr->is_dirty ) )
+ ||
+ ( prev_ptr->aux_next != next_ptr )
+ ||
+ ( prev_ptr->is_protected )
+ ||
+ ( prev_ptr->is_pinned ) ) {
+
+ /* something has happened to the dirty LRU -- start over
+ * from the tail.
+ */
+
+#if 0 /* This debuging code may be useful in the future -- keep it for now. */
+ if ( ! ( prev_ptr->is_dirty ) ) {
+ HDfprintf(stdout, "%s: ! prev_ptr->is_dirty\n",
+ fcn_name);
+ }
+ if ( prev_ptr->aux_next != next_ptr ) {
+ HDfprintf(stdout, "%s: prev_ptr->next != next_ptr\n",
+ fcn_name);
+ }
+ if ( prev_ptr->is_protected ) {
+ HDfprintf(stdout, "%s: prev_ptr->is_protected\n",
+ fcn_name);
+ }
+ if ( prev_ptr->is_pinned ) {
+ HDfprintf(stdout, "%s:prev_ptr->is_pinned\n",
+ fcn_name);
+ }
+
+ HDfprintf(stdout, "%s: re-starting scan of dirty list\n",
+ fcn_name);
+#endif /* JRM */
+ entry_ptr = cache_ptr->dLRU_tail_ptr;
+
+ } else {
+
+ entry_ptr = prev_ptr;
+
+ }
+ } else {
+
+ entry_ptr = NULL;
+
+ }
+
+ entries_examined++;
+ }
+
+#endif /* H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+ } else {
+
+ HDassert( H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS );
+
+ initial_list_len = cache_ptr->cLRU_list_len;
+ entry_ptr = cache_ptr->cLRU_tail_ptr;
+
+ while ( ( (cache_ptr->index_size + space_needed)
+ >
+ cache_ptr->max_cache_size
+ )
+ &&
+ ( entries_examined <= initial_list_len )
+ &&
+ ( entry_ptr != NULL )
+ )
+ {
+ HDassert( ! (entry_ptr->is_protected) );
+ HDassert( ! (entry_ptr->is_read_only) );
+ HDassert( (entry_ptr->ro_ref_count) == 0 );
+ HDassert( ! (entry_ptr->is_dirty) );
+
+ prev_ptr = entry_ptr->aux_prev;
+
+ result = H5C2_flush_single_entry(cache_ptr->f,
+ dxpl_id,
+ cache_ptr,
+ entry_ptr->type,
+ entry_ptr->addr,
+ H5C2__FLUSH_INVALIDATE_FLAG,
+ TRUE);
+
+ if ( result < 0 ) {
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "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++;
+ }
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_make_space_in_cache() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_validate_lru_list
+ *
+ * Purpose: Debugging function that scans the LRU list for errors.
+ *
+ * If an error is detected, the function generates a
+ * diagnostic and returns FAIL. If no error is detected,
+ * the function returns SUCCEED.
+ *
+ * Return: FAIL if error is detected, SUCCEED otherwise.
+ *
+ * Programmer: John Mainzer, 7/14/05
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+
+static herr_t
+H5C2_validate_lru_list(H5C2_t * cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ int32_t len = 0;
+ size_t size = 0;
+ H5C2_cache_entry_t * entry_ptr = NULL;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2_validate_lru_list)
+
+ HDassert( cache_ptr );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+
+ if ( ( ( cache_ptr->LRU_head_ptr == NULL )
+ ||
+ ( cache_ptr->LRU_tail_ptr == NULL )
+ )
+ &&
+ ( cache_ptr->LRU_head_ptr != cache_ptr->LRU_tail_ptr )
+ ) {
+
+ HDfprintf(stdout,"H5C2_validate_lru_list: Check 1 failed.\n");
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 1 failed")
+ }
+
+ if ( ( cache_ptr->LRU_list_len < 0 ) || ( cache_ptr->LRU_list_size < 0 ) ) {
+
+ HDfprintf(stdout,"H5C2_validate_lru_list: Check 2 failed.\n");
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 2 failed")
+ }
+
+ if ( ( cache_ptr->LRU_list_len == 1 )
+ &&
+ ( ( cache_ptr->LRU_head_ptr != cache_ptr->LRU_tail_ptr )
+ ||
+ ( cache_ptr->LRU_head_ptr == NULL )
+ ||
+ ( cache_ptr->LRU_head_ptr->size != cache_ptr->LRU_list_size )
+ )
+ ) {
+
+ HDfprintf(stdout,"H5C2_validate_lru_list: Check 3 failed.\n");
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 3 failed")
+ }
+
+ if ( ( cache_ptr->LRU_list_len >= 1 )
+ &&
+ ( ( cache_ptr->LRU_head_ptr == NULL )
+ ||
+ ( cache_ptr->LRU_head_ptr->prev != NULL )
+ ||
+ ( cache_ptr->LRU_tail_ptr == NULL )
+ ||
+ ( cache_ptr->LRU_tail_ptr->next != NULL )
+ )
+ ) {
+
+ HDfprintf(stdout,"H5C2_validate_lru_list: Check 4 failed.\n");
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 4 failed")
+ }
+
+ entry_ptr = cache_ptr->LRU_head_ptr;
+ while ( entry_ptr != NULL )
+ {
+
+ if ( ( entry_ptr != cache_ptr->LRU_head_ptr ) &&
+ ( ( entry_ptr->prev == NULL ) ||
+ ( entry_ptr->prev->next != entry_ptr ) ) ) {
+
+ HDfprintf(stdout,"H5C2_validate_lru_list: Check 5 failed.\n");
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 5 failed")
+ }
+
+ if ( ( entry_ptr != cache_ptr->LRU_tail_ptr ) &&
+ ( ( entry_ptr->next == NULL ) ||
+ ( entry_ptr->next->prev != entry_ptr ) ) ) {
+
+ HDfprintf(stdout,"H5C2_validate_lru_list: Check 6 failed.\n");
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 6 failed")
+ }
+
+ len++;
+ size += entry_ptr->size;
+ entry_ptr = entry_ptr->next;
+ }
+
+ if ( ( cache_ptr->LRU_list_len != len ) ||
+ ( cache_ptr->LRU_list_size != size ) ) {
+
+ HDfprintf(stdout,"H5C2_validate_lru_list: Check 7 failed.\n");
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 7 failed")
+ }
+
+done:
+
+ if ( ret_value != SUCCEED ) {
+
+ HDassert(0);
+ }
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_validate_lru_list() */
+
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C2_verify_not_in_index
+ *
+ * Purpose: Debugging function that scans the hash table to verify
+ * that the specified instance of H5C2_cache_entry_t is not
+ * present.
+ *
+ * If an error is detected, the function generates a
+ * diagnostic and returns FAIL. If no error is detected,
+ * the function returns SUCCEED.
+ *
+ * Return: FAIL if error is detected, SUCCEED otherwise.
+ *
+ * Programmer: John Mainzer, 7/14/05
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C2_DO_EXTREME_SANITY_CHECKS
+
+static herr_t
+H5C2_verify_not_in_index(H5C2_t * cache_ptr,
+ H5C2_cache_entry_t * entry_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ int32_t i;
+ int32_t depth;
+ H5C2_cache_entry_t * scan_ptr = NULL;
+
+ FUNC_ENTER_NOAPI_NOINIT(H5C2_verify_not_in_index)
+
+ HDassert( cache_ptr != NULL );
+ HDassert( cache_ptr->magic == H5C2__H5C2_T_MAGIC );
+ HDassert( entry_ptr != NULL );
+
+ for ( i = 0; i < H5C2__HASH_TABLE_LEN; i++ )
+ {
+ depth = 0;
+ scan_ptr = cache_ptr->index[i];
+
+ while ( scan_ptr != NULL )
+ {
+ if ( scan_ptr == entry_ptr ) {
+
+ HDfprintf(stdout,
+ "H5C2_verify_not_in_index: entry in index (%d/%d)\n",
+ i, depth);
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Entry already in index.")
+ }
+ depth++;
+ scan_ptr = scan_ptr->ht_next;
+ }
+ }
+
+done:
+
+ if ( ret_value != SUCCEED ) {
+
+ HDassert(0);
+ }
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C2_verify_not_in_index() */
+
+#endif /* H5C2_DO_EXTREME_SANITY_CHECKS */
diff --git a/src/H5C2pkg.h b/src/H5C2pkg.h
new file mode 100644
index 0000000..a1753b4
--- /dev/null
+++ b/src/H5C2pkg.h
@@ -0,0 +1,949 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+ * Copyright by the Board of Trustees of the University of Illinois. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * Programmer: John Mainzer -- 10/12/04
+ *
+ * Purpose: This file contains declarations which are normally visible
+ * only within the H5C2 package (just H5C2.c at present).
+ *
+ * Source files outside the H5C2 package should include
+ * H5C2private.h instead.
+ *
+ * The one exception to this rule is test/cache2.c. The test
+ * code is easier to write if it can look at the cache's
+ * internal data structures. Indeed, this is the main
+ * reason why this file was created.
+ */
+
+#ifndef H5C2_PACKAGE
+#error "Do not include this file outside the H5C2 package!"
+#endif
+
+#ifndef _H5C2pkg_H
+#define _H5C2pkg_H
+
+
+/* Get package's private header */
+#include "H5C2private.h"
+
+
+/* Get needed headers */
+#include "H5SLprivate.h" /* Skip lists */
+
+/* With the introduction of the fractal heap, it is now possible for
+ * entries to be dirtied, resized, and/or renamed in the flush callbacks.
+ * As a result, on flushes, it may be necessary to make multiple passes
+ * through the slist before it is empty. The H5C2__MAX_PASSES_ON_FLUSH
+ * #define is used to set an upper limit on the number of passes.
+ * The current value was obtained via personal communication with
+ * Quincey. I have applied a fudge factor of 2.
+ */
+
+#define H5C2__MAX_PASSES_ON_FLUSH 4
+
+
+#define H5C2__HASH_TABLE_LEN (64 * 1024) /* must be a power of 2 */
+
+
+/****************************************************************************
+ *
+ * structure H5C2_t
+ *
+ * Catchall structure for all variables specific to an instance of the cache.
+ *
+ * While the individual fields of the structure are discussed below, the
+ * following overview may be helpful.
+ *
+ * Entries in the cache are stored in an instance of H5TB_TREE, indexed on
+ * the entry's disk address. While the H5TB_TREE is less efficient than
+ * hash table, it keeps the entries in address sorted order. As flushes
+ * in parallel mode are more efficient if they are issued in increasing
+ * address order, this is a significant benefit. Also the H5TB_TREE code
+ * was readily available, which reduced development time.
+ *
+ * While the cache was designed with multiple replacement policies in mind,
+ * at present only a modified form of LRU is supported.
+ *
+ * JRM - 4/26/04
+ *
+ * Profiling has indicated that searches in the instance of H5TB_TREE are
+ * too expensive. To deal with this issue, I have augmented the cache
+ * with a hash table in which all entries will be stored. Given the
+ * advantages of flushing entries in increasing address order, the TBBT
+ * is retained, but only dirty entries are stored in it. At least for
+ * now, we will leave entries in the TBBT after they are flushed.
+ *
+ * Note that index_size and index_len now refer to the total size of
+ * and number of entries in the hash table.
+ *
+ * JRM - 7/19/04
+ *
+ * The TBBT has since been replaced with a skip list. This change
+ * greatly predates this note.
+ *
+ * JRM - 9/26/05
+ *
+ * magic: Unsigned 32 bit integer always set to H5C2__H5C2_T_MAGIC.
+ * This field is used to validate pointers to instances of
+ * H5C2_t.
+ *
+ * f: Pointer to the instance of H5F_t associated with this
+ * instance of the metadata cache. This field is set at
+ * create, and then used until the file is closed (at which
+ * point, the cache will be shut down as well).
+ *
+ * flush_in_progress: Boolean flag indicating whether a flush is in
+ * progress.
+ *
+ * trace_file_ptr: File pointer pointing to the trace file, which is used
+ * to record cache operations for use in simulations and design
+ * studies. This field will usually be NULL, indicating that
+ * no trace file should be recorded.
+ *
+ * Since much of the code supporting the parallel metadata
+ * cache is in H5AC, we don't write the trace file from
+ * H5C2. Instead, H5AC reads the trace_file_ptr as needed.
+ *
+ * When we get to using H5C2 in other places, we may add
+ * code to write trace file data at the H5C2 level as well.
+ *
+ * aux_ptr: Pointer to void used to allow wrapper code to associate
+ * its data with an instance of H5C2_t. The H5C2 cache code
+ * sets this field to NULL, and otherwise leaves it alone.
+ *
+ * max_type_id: Integer field containing the maximum type id number assigned
+ * to a type of entry in the cache. All type ids from 0 to
+ * max_type_id inclusive must be defined. The names of the
+ * types are stored in the type_name_table discussed below, and
+ * indexed by the ids.
+ *
+ * type_name_table_ptr: Pointer to an array of pointer to char of length
+ * max_type_id + 1. The strings pointed to by the entries
+ * in the array are the names of the entry types associated
+ * with the indexing type IDs.
+ *
+ * max_cache_size: Nominal maximum number of bytes that may be stored in the
+ * cache. This value should be viewed as a soft limit, as the
+ * cache can exceed this value under the following circumstances:
+ *
+ * a) All entries in the cache are protected, and the cache is
+ * asked to insert a new entry. In this case the new entry
+ * will be created. If this causes the cache to exceed
+ * max_cache_size, it will do so. The cache will attempt
+ * to reduce its size as entries are unprotected.
+ *
+ * b) When running in parallel mode, the cache may not be
+ * permitted to flush a dirty entry in response to a read.
+ * If there are no clean entries available to evict, the
+ * cache will exceed its maximum size. Again the cache
+ * will attempt to reduce its size to the max_cache_size
+ * limit on the next cache write.
+ *
+ * c) When an entry increases in size, the cache may exceed
+ * the max_cache_size limit until the next time the cache
+ * attempts to load or insert an entry.
+ *
+ * min_clean_size: Nominal minimum number of clean bytes in the cache.
+ * The cache attempts to maintain this number of bytes of
+ * clean data so as to avoid case b) above. Again, this is
+ * a soft limit.
+ *
+ *
+ * In addition to the call back functions required for each entry, the
+ * cache requires the following call back functions for this instance of
+ * the cache as a whole:
+ *
+ * check_write_permitted: In certain applications, the cache may not
+ * be allowed to write to disk at certain time. If specified,
+ * the check_write_permitted function is used to determine if
+ * a write is permissible at any given point in time.
+ *
+ * If no such function is specified (i.e. this field is NULL),
+ * the cache uses the following write_permitted field to
+ * determine whether writes are permitted.
+ *
+ * write_permitted: If check_write_permitted is NULL, this boolean flag
+ * indicates whether writes are permitted.
+ *
+ * log_flush: If provided, this function is called whenever a dirty
+ * entry is flushed to disk.
+ *
+ *
+ * In cases where memory is plentiful, and performance is an issue, it
+ * is useful to disable all cache evictions, and thereby postpone metadata
+ * writes. The following field is used to implement this.
+ *
+ * evictions_enabled: Boolean flag that is initialized to TRUE. When
+ * this flag is set to FALSE, the metadata cache will not
+ * attempt to evict entries to make space for newly protected
+ * entries, and instead the will grow without limit.
+ *
+ * Needless to say, this feature must be used with care.
+ *
+ *
+ * The cache requires an index to facilitate searching for entries. The
+ * following fields support that index.
+ *
+ * index_len: Number of entries currently in the hash table used to index
+ * the cache.
+ *
+ * index_size: Number of bytes of cache entries currently stored in the
+ * hash table used to index the cache.
+ *
+ * This value should not be mistaken for footprint of the
+ * cache in memory. The average cache entry is small, and
+ * the cache has a considerable overhead. Multiplying the
+ * index_size by two should yield a conservative estimate
+ * of the cache's memory footprint.
+ *
+ * index: Array of pointer to H5C2_cache_entry_t of size
+ * H5C2__HASH_TABLE_LEN. At present, this value is a power
+ * of two, not the usual prime number.
+ *
+ * I hope that the variable size of cache elements, the large
+ * hash table size, and the way in which HDF5 allocates space
+ * will combine to avoid problems with periodicity. If so, we
+ * can use a trivial hash function (a bit-and and a 3 bit left
+ * shift) with some small savings.
+ *
+ * If not, it will become evident in the statistics. Changing
+ * to the usual prime number length hash table will require
+ * changing the H5C2__HASH_FCN macro and the deletion of the
+ * H5C2__HASH_MASK #define. No other changes should be required.
+ *
+ *
+ * When we flush the cache, we need to write entries out in increasing
+ * address order. An instance of a skip list is used to store dirty entries in
+ * sorted order. Whether it is cheaper to sort the dirty entries as needed,
+ * or to maintain the list is an open question. At a guess, it depends
+ * on how frequently the cache is flushed. We will see how it goes.
+ *
+ * For now at least, I will not remove dirty entries from the list as they
+ * are flushed. (this has been changed -- dirty entries are now removed from
+ * the skip list as they are flushed. JRM - 10/25/05)
+ *
+ * slist_len: Number of entries currently in the skip list
+ * used to maintain a sorted list of dirty entries in the
+ * cache.
+ *
+ * slist_size: Number of bytes of cache entries currently stored in the
+ * skip list used to maintain a sorted list of
+ * dirty entries in the cache.
+ *
+ * slist_ptr: pointer to the instance of H5SL_t used maintain a sorted
+ * list of dirty entries in the cache. This sorted list has
+ * two uses:
+ *
+ * a) It allows us to flush dirty entries in increasing address
+ * order, which results in significant savings.
+ *
+ * b) It facilitates checking for adjacent dirty entries when
+ * attempting to evict entries from the cache. While we
+ * don't use this at present, I hope that this will allow
+ * some optimizations when I get to it.
+ *
+ * With the addition of the fractal heap, the cache must now deal with
+ * the case in which entries may be dirtied, renamed, or have their sizes
+ * changed during a flush. To allow sanity checks in this situation, the
+ * following two fields have been added. They are only compiled in when
+ * H5C2_DO_SANITY_CHECKS is TRUE.
+ *
+ * slist_len_increase: Number of entries that have been added to the
+ * slist since the last time this field was set to zero.
+ *
+ * slist_size_increase: Total size of all entries that have been added
+ * to the slist since the last time this field was set to
+ * zero.
+ *
+ *
+ * When a cache entry is protected, it must be removed from the LRU
+ * list(s) as it cannot be either flushed or evicted until it is unprotected.
+ * The following fields are used to implement the protected list (pl).
+ *
+ * pl_len: Number of entries currently residing on the protected list.
+ *
+ * pl_size: Number of bytes of cache entries currently residing on the
+ * protected list.
+ *
+ * pl_head_ptr: Pointer to the head of the doubly linked list of protected
+ * entries. Note that cache entries on this list are linked
+ * by their next and prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * pl_tail_ptr: Pointer to the tail of the doubly linked list of protected
+ * entries. Note that cache entries on this list are linked
+ * by their next and prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ *
+ * For very frequently used entries, the protect/unprotect overhead can
+ * become burdensome. To avoid this overhead, I have modified the cache
+ * to allow entries to be "pinned". A pinned entry is similar to a
+ * protected entry, in the sense that it cannot be evicted, and that
+ * the entry can be modified at any time.
+ *
+ * Pinning an entry has the following implications:
+ *
+ * 1) A pinned entry cannot be evicted. Thus unprotected
+ * pinned entries reside in the pinned entry list, instead
+ * of the LRU list(s) (or other lists maintained by the current
+ * replacement policy code).
+ *
+ * 2) A pinned entry can be accessed or modified at any time.
+ * Therefore, the cache must check with the entry owner
+ * before flushing it. If permission is denied, the
+ * cache just skips the entry in the flush.
+ *
+ * 3) A pinned entry can be marked as dirty (and possibly
+ * change size) while it is unprotected.
+ *
+ * 4) The flush-destroy code must allow pinned entries to
+ * be unpinned (and possibly unprotected) during the
+ * flush.
+ *
+ * Since pinned entries cannot be evicted, they must be kept on a pinned
+ * entry list, instead of being entrusted to the replacement policy code.
+ *
+ * Maintaining the pinned entry list requires the following fields:
+ *
+ * pel_len: Number of entries currently residing on the pinned
+ * entry list.
+ *
+ * pel_size: Number of bytes of cache entries currently residing on
+ * the pinned entry list.
+ *
+ * pel_head_ptr: Pointer to the head of the doubly linked list of pinned
+ * but not protected entries. Note that cache entries on
+ * this list are linked by their next and prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * pel_tail_ptr: Pointer to the tail of the doubly linked list of pinned
+ * but not protected entries. Note that cache entries on
+ * this list are linked by their next and prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ *
+ * The cache must have a replacement policy, and the fields supporting this
+ * policy must be accessible from this structure.
+ *
+ * While there has been interest in several replacement policies for
+ * this cache, the initial development schedule is tight. Thus I have
+ * elected to support only a modified LRU policy for the first cut.
+ *
+ * To further simplify matters, I have simply included the fields needed
+ * by the modified LRU in this structure. When and if we add support for
+ * other policies, it will probably be easiest to just add the necessary
+ * fields to this structure as well -- we only create one instance of this
+ * structure per file, so the overhead is not excessive.
+ *
+ *
+ * Fields supporting the modified LRU policy:
+ *
+ * See most any OS text for a discussion of the LRU replacement policy.
+ *
+ * When operating in parallel mode, we must ensure that a read does not
+ * cause a write. If it does, the process will hang, as the write will
+ * be collective and the other processes will not know to participate.
+ *
+ * To deal with this issue, I have modified the usual LRU policy by adding
+ * clean and dirty LRU lists to the usual LRU list.
+ *
+ * The clean LRU list is simply the regular LRU list with all dirty cache
+ * entries removed.
+ *
+ * Similarly, the dirty LRU list is the regular LRU list with all the clean
+ * cache entries removed.
+ *
+ * When reading in parallel mode, we evict from the clean LRU list only.
+ * This implies that we must try to ensure that the clean LRU list is
+ * reasonably well stocked at all times.
+ *
+ * We attempt to do this by trying to flush enough entries on each write
+ * to keep the cLRU_list_size >= min_clean_size.
+ *
+ * Even if we start with a completely clean cache, a sequence of protects
+ * without unprotects can empty the clean LRU list. In this case, the
+ * cache must grow temporarily. At the next write, we will attempt to
+ * evict enough entries to reduce index_size to less than max_cache_size.
+ * While this will usually be possible, all bets are off if enough entries
+ * are protected.
+ *
+ * Discussions of the individual fields used by the modified LRU replacement
+ * policy follow:
+ *
+ * LRU_list_len: Number of cache entries currently on the LRU list.
+ *
+ * Observe that LRU_list_len + pl_len must always equal
+ * index_len.
+ *
+ * LRU_list_size: Number of bytes of cache entries currently residing on the
+ * LRU list.
+ *
+ * Observe that LRU_list_size + pl_size must always equal
+ * index_size.
+ *
+ * LRU_head_ptr: Pointer to the head of the doubly linked LRU list. Cache
+ * entries on this list are linked by their next and prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * LRU_tail_ptr: Pointer to the tail of the doubly linked LRU list. Cache
+ * entries on this list are linked by their next and prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * cLRU_list_len: Number of cache entries currently on the clean LRU list.
+ *
+ * Observe that cLRU_list_len + dLRU_list_len must always
+ * equal LRU_list_len.
+ *
+ * cLRU_list_size: Number of bytes of cache entries currently residing on
+ * the clean LRU list.
+ *
+ * Observe that cLRU_list_size + dLRU_list_size must always
+ * equal LRU_list_size.
+ *
+ * cLRU_head_ptr: Pointer to the head of the doubly linked clean LRU list.
+ * Cache entries on this list are linked by their aux_next and
+ * aux_prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * cLRU_tail_ptr: Pointer to the tail of the doubly linked clean LRU list.
+ * Cache entries on this list are linked by their aux_next and
+ * aux_prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * dLRU_list_len: Number of cache entries currently on the dirty LRU list.
+ *
+ * Observe that cLRU_list_len + dLRU_list_len must always
+ * equal LRU_list_len.
+ *
+ * dLRU_list_size: Number of cache entries currently on the dirty LRU list.
+ *
+ * Observe that cLRU_list_len + dLRU_list_len must always
+ * equal LRU_list_len.
+ *
+ * dLRU_head_ptr: Pointer to the head of the doubly linked dirty LRU list.
+ * Cache entries on this list are linked by their aux_next and
+ * aux_prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * dLRU_tail_ptr: Pointer to the tail of the doubly linked dirty LRU list.
+ * Cache entries on this list are linked by their aux_next and
+ * aux_prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ *
+ * Automatic cache size adjustment:
+ *
+ * While the default cache size is adequate for most cases, we can run into
+ * cases where the default is too small. Ideally, we will let the user
+ * adjust the cache size as required. However, this is not possible in all
+ * cases. Thus I have added automatic cache size adjustment code.
+ *
+ * The configuration for the automatic cache size adjustment is stored in
+ * the structure described below:
+ *
+ * size_increase_possible: Depending on the configuration data given
+ * in the resize_ctl field, it may or may not be possible
+ * to increase the size of the cache. Rather than test for
+ * all the ways this can happen, we simply set this flag when
+ * we receive a new configuration.
+ *
+ * size_decrease_possible: Depending on the configuration data given
+ * in the resize_ctl field, it may or may not be possible
+ * to decrease the size of the cache. Rather than test for
+ * all the ways this can happen, we simply set this flag when
+ * we receive a new configuration.
+ *
+ * cache_full: Boolean flag used to keep track of whether the cache is
+ * full, so we can refrain from increasing the size of a
+ * cache which hasn't used up the space alotted to it.
+ *
+ * The field is initialized to FALSE, and then set to TRUE
+ * whenever we attempt to make space in the cache.
+ *
+ * resize_enabled: This is another convenience flag which is set whenever
+ * a new set of values for resize_ctl are provided. Very
+ * simply,
+ *
+ * resize_enabled = size_increase_possible ||
+ * size_decrease_possible;
+ *
+ * size_decreased: Boolean flag set to TRUE whenever the maximun cache
+ * size is decreased. The flag triggers a call to
+ * H5C2_make_space_in_cache() on the next call to H5C2_protect().
+ *
+ * resize_ctl: Instance of H5C2_auto_size_ctl_t containing configuration
+ * data for automatic cache resizing.
+ *
+ * epoch_markers_active: Integer field containing the number of epoch
+ * markers currently in use in the LRU list. This value
+ * must be in the range [0, H5C2__MAX_EPOCH_MARKERS - 1].
+ *
+ * epoch_marker_active: Array of boolean of length H5C2__MAX_EPOCH_MARKERS.
+ * This array is used to track which epoch markers are currently
+ * in use.
+ *
+ * epoch_marker_ringbuf: Array of int of length H5C2__MAX_EPOCH_MARKERS + 1.
+ *
+ * To manage the epoch marker cache entries, it is necessary
+ * to track their order in the LRU list. This is done with
+ * epoch_marker_ringbuf. When markers are inserted at the
+ * head of the LRU list, the index of the marker in the
+ * epoch_markers array is inserted at the tail of the ring
+ * buffer. When it becomes the epoch_marker_active'th marker
+ * in the LRU list, it will have worked its way to the head
+ * of the ring buffer as well. This allows us to remove it
+ * without scanning the LRU list if such is required.
+ *
+ * epoch_marker_ringbuf_first: Integer field containing the index of the
+ * first entry in the ring buffer.
+ *
+ * epoch_marker_ringbuf_last: Integer field containing the index of the
+ * last entry in the ring buffer.
+ *
+ * epoch_marker_ringbuf_size: Integer field containing the number of entries
+ * in the ring buffer.
+ *
+ * epoch_markers: Array of instances of H5C2_cache_entry_t of length
+ * H5C2__MAX_EPOCH_MARKERS. The entries are used as markers
+ * in the LRU list to identify cache entries that haven't
+ * been accessed for some (small) specified number of
+ * epochs. These entries (if any) can then be evicted and
+ * the cache size reduced -- ideally without evicting any
+ * of the current working set. Needless to say, the epoch
+ * length and the number of epochs before an unused entry
+ * must be chosen so that all, or almost all, the working
+ * set will be accessed before the limit.
+ *
+ * Epoch markers only appear in the LRU list, never in
+ * the index or slist. While they are of type
+ * H5C2__EPOCH_MARKER_TYPE, and have associated class
+ * functions, these functions should never be called.
+ *
+ * The addr fields of these instances of H5C2_cache_entry_t
+ * are set to the index of the instance in the epoch_markers
+ * array, the size is set to 0, and the type field points
+ * to the constant structure epoch_marker_class defined
+ * in H5C2.c. The next and prev fields are used as usual
+ * to link the entry into the LRU list.
+ *
+ * All other fields are unused.
+ *
+ *
+ * Cache hit rate collection fields:
+ *
+ * We supply the current cache hit rate on request, so we must keep a
+ * simple cache hit rate computation regardless of whether statistics
+ * collection is enabled. The following fields support this capability.
+ *
+ * cache_hits: Number of cache hits since the last time the cache hit
+ * rate statistics were reset. Note that when automatic cache
+ * re-sizing is enabled, this field will be reset every automatic
+ * resize epoch.
+ *
+ * cache_accesses: Number of times the cache has been accessed while
+ * since the last since the last time the cache hit rate statistics
+ * were reset. Note that when automatic cache re-sizing is enabled,
+ * this field will be reset every automatic resize epoch.
+ *
+ *
+ * Statistics collection fields:
+ *
+ * When enabled, these fields are used to collect statistics as described
+ * below. The first set are collected only when H5C2_COLLECT_CACHE_STATS
+ * is true.
+ *
+ * hits: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the number of times an entry with type id
+ * equal to the array index has been in cache when requested in
+ * the current epoch.
+ *
+ * misses: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the number of times an entry with type id
+ * equal to the array index has not been in cache when
+ * requested in the current epoch.
+ *
+ * write_protects: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The
+ * cells are used to record the number of times an entry with
+ * type id equal to the array index has been write protected
+ * in the current epoch.
+ *
+ * Observe that (hits + misses) = (write_protects + read_protects).
+ *
+ * read_protects: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The
+ * cells are used to record the number of times an entry with
+ * type id equal to the array index has been read protected in
+ * the current epoch.
+ *
+ * Observe that (hits + misses) = (write_protects + read_protects).
+ *
+ * max_read_protects: Array of int32 of length H5C2__MAX_NUM_TYPE_IDS + 1.
+ * The cells are used to maximum number of simultaneous read
+ * protects on any entry with type id equal to the array index
+ * in the current epoch.
+ *
+ * insertions: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the number of times an entry with type
+ * id equal to the array index has been inserted into the
+ * cache in the current epoch.
+ *
+ * pinned_insertions: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1.
+ * The cells are used to record the number of times an entry
+ * with type id equal to the array index has been inserted
+ * pinned into the cache in the current epoch.
+ *
+ * clears: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the number of times an entry with type
+ * id equal to the array index has been cleared in the current
+ * epoch.
+ *
+ * flushes: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the number of times an entry with type id
+ * equal to the array index has been written to disk in the
+ * current epoch.
+ *
+ * evictions: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the number of times an entry with type id
+ * equal to the array index has been evicted from the cache in
+ * the current epoch.
+ *
+ * renames: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the number of times an entry with type
+ * id equal to the array index has been renamed in the current
+ * epoch.
+ *
+ * entry_flush_renames: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1.
+ * The cells are used to record the number of times an entry
+ * with type id equal to the array index has been renamed
+ * during its flush callback in the current epoch.
+ *
+ * cache_flush_renames: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1.
+ * The cells are used to record the number of times an entry
+ * with type id equal to the array index has been renamed
+ * during a cache flush in the current epoch.
+ *
+ * pins: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the number of times an entry with type
+ * id equal to the array index has been pinned in the current
+ * epoch.
+ *
+ * unpins: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the number of times an entry with type
+ * id equal to the array index has been unpinned in the current
+ * epoch.
+ *
+ * dirty_pins: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the number of times an entry with type
+ * id equal to the array index has been marked dirty while pinned
+ * in the current epoch.
+ *
+ * pinned_flushes: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The
+ * cells are used to record the number of times an entry
+ * with type id equal to the array index has been flushed while
+ * pinned in the current epoch.
+ *
+ * pinned_cleared: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1. The
+ * cells are used to record the number of times an entry
+ * with type id equal to the array index has been cleared while
+ * pinned in the current epoch.
+ *
+ * size_increases: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1.
+ * The cells are used to record the number of times an entry
+ * with type id equal to the array index has increased in
+ * size in the current epoch.
+ *
+ * size_decreases: Array of int64 of length H5C2__MAX_NUM_TYPE_IDS + 1.
+ * The cells are used to record the number of times an entry
+ * with type id equal to the array index has decreased in
+ * size in the current epoch.
+ *
+ * entry_flush_size_changes: Array of int64 of length
+ * H5C2__MAX_NUM_TYPE_IDS + 1. The cells are used to record
+ * the number of times an entry with type id equal to the
+ * array index has changed size while in its flush callback.
+ *
+ * cache_flush_size_changes: Array of int64 of length
+ * H5C2__MAX_NUM_TYPE_IDS + 1. The cells are used to record
+ * the number of times an entry with type id equal to the
+ * array index has changed size during a cache flush
+ *
+ * total_ht_insertions: Number of times entries have been inserted into the
+ * hash table in the current epoch.
+ *
+ * total_ht_deletions: Number of times entries have been deleted from the
+ * hash table in the current epoch.
+ *
+ * successful_ht_searches: int64 containing the total number of successful
+ * searches of the hash table in the current epoch.
+ *
+ * total_successful_ht_search_depth: int64 containing the total number of
+ * entries other than the targets examined in successful
+ * searches of the hash table in the current epoch.
+ *
+ * failed_ht_searches: int64 containing the total number of unsuccessful
+ * searches of the hash table in the current epoch.
+ *
+ * total_failed_ht_search_depth: int64 containing the total number of
+ * entries examined in unsuccessful searches of the hash
+ * table in the current epoch.
+ *
+ * max_index_len: Largest value attained by the index_len field in the
+ * current epoch.
+ *
+ * max_index_size: Largest value attained by the index_size field in the
+ * current epoch.
+ *
+ * max_slist_len: Largest value attained by the slist_len field in the
+ * current epoch.
+ *
+ * max_slist_size: Largest value attained by the slist_size field in the
+ * current epoch.
+ *
+ * max_pl_len: Largest value attained by the pl_len field in the
+ * current epoch.
+ *
+ * max_pl_size: Largest value attained by the pl_size field in the
+ * current epoch.
+ *
+ * max_pel_len: Largest value attained by the pel_len field in the
+ * current epoch.
+ *
+ * max_pel_size: Largest value attained by the pel_size field in the
+ * current epoch.
+ *
+ * The remaining stats are collected only when both H5C2_COLLECT_CACHE_STATS
+ * and H5C2_COLLECT_CACHE_ENTRY_STATS are true.
+ *
+ * max_accesses: Array of int32 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the maximum number of times any single
+ * entry with type id equal to the array index has been
+ * accessed in the current epoch.
+ *
+ * min_accesses: Array of int32 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the minimum number of times any single
+ * entry with type id equal to the array index has been
+ * accessed in the current epoch.
+ *
+ * max_clears: Array of int32 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the maximum number of times any single
+ * entry with type id equal to the array index has been cleared
+ * in the current epoch.
+ *
+ * max_flushes: Array of int32 of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the maximum number of times any single
+ * entry with type id equal to the array index has been
+ * flushed in the current epoch.
+ *
+ * max_size: Array of size_t of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the maximum size of any single entry
+ * with type id equal to the array index that has resided in
+ * the cache in the current epoch.
+ *
+ * max_pins: Array of size_t of length H5C2__MAX_NUM_TYPE_IDS + 1. The cells
+ * are used to record the maximum number of times that any single
+ * entry with type id equal to the array index that has been
+ * marked as pinned in the cache in the current epoch.
+ *
+ *
+ * Fields supporting testing:
+ *
+ * For test purposes, it is useful to turn off some asserts and sanity
+ * checks. The following flags support this.
+ *
+ * skip_file_checks: Boolean flag used to skip sanity checks on file
+ * parameters passed to the cache. In the test bed, there
+ * is no reason to have a file open, as the cache proper
+ * just passes these parameters through without using them.
+ *
+ * When this flag is set, all sanity checks on the file
+ * parameters are skipped. The field defaults to FALSE.
+ *
+ * skip_dxpl_id_checks: Boolean flag used to skip sanity checks on the
+ * dxpl_id parameters passed to the cache. These are not
+ * used directly by the cache, so skipping the checks
+ * simplifies the test bed.
+ *
+ * When this flag is set, all sanity checks on the dxpl_id
+ * parameters are skipped. The field defaults to FALSE.
+ *
+ * prefix Array of char used to prefix debugging output. The
+ * field is intended to allow marking of output of with
+ * the processes mpi rank.
+ *
+ ****************************************************************************/
+
+#define H5C2__H5C2_T_MAGIC 0x005CAC0F
+#define H5C2__MAX_NUM_TYPE_IDS 16
+#define H5C2__PREFIX_LEN 32
+
+struct H5C2_t
+{
+ uint32_t magic;
+
+ H5F_t * f;
+
+ hbool_t flush_in_progress;
+
+ FILE * trace_file_ptr;
+
+ void * aux_ptr;
+
+ int32_t max_type_id;
+ const char * (* type_name_table_ptr);
+
+ size_t max_cache_size;
+ size_t min_clean_size;
+
+ H5C2_write_permitted_func_t check_write_permitted;
+ hbool_t write_permitted;
+
+ H5C2_log_flush_func_t log_flush;
+
+ hbool_t evictions_enabled;
+
+ int32_t index_len;
+ size_t index_size;
+ H5C2_cache_entry_t * (index[H5C2__HASH_TABLE_LEN]);
+
+
+ int32_t slist_len;
+ size_t slist_size;
+ H5SL_t * slist_ptr;
+#if H5C2_DO_SANITY_CHECKS
+ int64_t slist_len_increase;
+ int64_t slist_size_increase;
+#endif /* H5C2_DO_SANITY_CHECKS */
+
+ int32_t pl_len;
+ size_t pl_size;
+ H5C2_cache_entry_t * pl_head_ptr;
+ H5C2_cache_entry_t * pl_tail_ptr;
+
+ int32_t pel_len;
+ size_t pel_size;
+ H5C2_cache_entry_t * pel_head_ptr;
+ H5C2_cache_entry_t * pel_tail_ptr;
+
+ int32_t LRU_list_len;
+ size_t LRU_list_size;
+ H5C2_cache_entry_t * LRU_head_ptr;
+ H5C2_cache_entry_t * LRU_tail_ptr;
+
+ int32_t cLRU_list_len;
+ size_t cLRU_list_size;
+ H5C2_cache_entry_t * cLRU_head_ptr;
+ H5C2_cache_entry_t * cLRU_tail_ptr;
+
+ int32_t dLRU_list_len;
+ size_t dLRU_list_size;
+ H5C2_cache_entry_t * dLRU_head_ptr;
+ H5C2_cache_entry_t * dLRU_tail_ptr;
+
+ hbool_t size_increase_possible;
+ hbool_t size_decrease_possible;
+ hbool_t resize_enabled;
+ hbool_t cache_full;
+ hbool_t size_decreased;
+ H5C2_auto_size_ctl_t resize_ctl;
+
+ int32_t epoch_markers_active;
+ hbool_t epoch_marker_active[H5C2__MAX_EPOCH_MARKERS];
+ int32_t epoch_marker_ringbuf[H5C2__MAX_EPOCH_MARKERS+1];
+ int32_t epoch_marker_ringbuf_first;
+ int32_t epoch_marker_ringbuf_last;
+ int32_t epoch_marker_ringbuf_size;
+ H5C2_cache_entry_t epoch_markers[H5C2__MAX_EPOCH_MARKERS];
+
+ int64_t cache_hits;
+ int64_t cache_accesses;
+
+#if H5C2_COLLECT_CACHE_STATS
+
+ /* stats fields */
+ int64_t hits[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t misses[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t write_protects[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t read_protects[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int32_t max_read_protects[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t insertions[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t pinned_insertions[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t clears[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t flushes[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t evictions[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t renames[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t entry_flush_renames[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t cache_flush_renames[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t pins[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t unpins[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t dirty_pins[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t pinned_flushes[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t pinned_clears[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t size_increases[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t size_decreases[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t entry_flush_size_changes
+ [H5C2__MAX_NUM_TYPE_IDS + 1];
+ int64_t cache_flush_size_changes
+ [H5C2__MAX_NUM_TYPE_IDS + 1];
+
+ int64_t total_ht_insertions;
+ int64_t total_ht_deletions;
+ int64_t successful_ht_searches;
+ int64_t total_successful_ht_search_depth;
+ int64_t failed_ht_searches;
+ int64_t total_failed_ht_search_depth;
+
+ int32_t max_index_len;
+ size_t max_index_size;
+
+ int32_t max_slist_len;
+ size_t max_slist_size;
+
+ int32_t max_pl_len;
+ size_t max_pl_size;
+
+ int32_t max_pel_len;
+ size_t max_pel_size;
+
+#if H5C2_COLLECT_CACHE_ENTRY_STATS
+
+ int32_t max_accesses[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int32_t min_accesses[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int32_t max_clears[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int32_t max_flushes[H5C2__MAX_NUM_TYPE_IDS + 1];
+ size_t max_size[H5C2__MAX_NUM_TYPE_IDS + 1];
+ int32_t max_pins[H5C2__MAX_NUM_TYPE_IDS + 1];
+
+#endif /* H5C2_COLLECT_CACHE_ENTRY_STATS */
+
+#endif /* H5C2_COLLECT_CACHE_STATS */
+
+ hbool_t skip_file_checks;
+ hbool_t skip_dxpl_id_checks;
+ char prefix[H5C2__PREFIX_LEN];
+};
+
+#endif /* _H5C2pkg_H */
+
diff --git a/src/H5C2private.h b/src/H5C2private.h
new file mode 100644
index 0000000..a949f6d
--- /dev/null
+++ b/src/H5C2private.h
@@ -0,0 +1,1350 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+ * Copyright by the Board of Trustees of the University of Illinois. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5C2private.h
+ * 6/3/04
+ * John Mainzer
+ *
+ * Purpose: Constants and typedefs available to the rest of the
+ * library.
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef _H5C2private_H
+#define _H5C2private_H
+
+#include "H5C2public.h" /* public prototypes */
+
+/* Pivate headers needed by this header */
+#include "H5private.h" /* Generic Functions */
+#include "H5Fprivate.h" /* File access */
+
+
+#define H5C2_DO_SANITY_CHECKS 1
+#define H5C2_DO_EXTREME_SANITY_CHECKS 0
+
+/* This sanity checking constant was picked out of the air. Increase
+ * or decrease it if appropriate. Its purposes is to detect corrupt
+ * object sizes, so it probably doesn't matter if it is a bit big.
+ *
+ * JRM - 5/17/04
+ */
+#define H5C2_MAX_ENTRY_SIZE ((size_t)(10 * 1024 * 1024))
+
+/* H5C2_COLLECT_CACHE_STATS controls overall collection of statistics
+ * on cache activity. In general, this #define should be set to 0.
+ */
+#define H5C2_COLLECT_CACHE_STATS 1
+
+/* H5C2_COLLECT_CACHE_ENTRY_STATS controls collection of statistics
+ * in individual cache entries.
+ *
+ * H5C2_COLLECT_CACHE_ENTRY_STATS should only be defined to true if
+ * H5C2_COLLECT_CACHE_STATS is also defined to true.
+ */
+#if H5C2_COLLECT_CACHE_STATS
+
+#define H5C2_COLLECT_CACHE_ENTRY_STATS 1
+
+#else
+
+#define H5C2_COLLECT_CACHE_ENTRY_STATS 0
+
+#endif /* H5C2_COLLECT_CACHE_STATS */
+
+
+#ifdef H5_HAVE_PARALLEL
+
+/* we must maintain the clean and dirty LRU lists when we are compiled
+ * with parallel support.
+ */
+#define H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS 1
+
+#else /* H5_HAVE_PARALLEL */
+
+/* The clean and dirty LRU lists don't buy us anything here -- we may
+ * want them on for testing on occasion, but in general they should be
+ * off.
+ */
+#define H5C2_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS 0
+
+#endif /* H5_HAVE_PARALLEL */
+
+
+/* Typedef for the main structure for the cache (defined in H5C2pkg.h) */
+
+typedef struct H5C2_t H5C2_t;
+
+
+/***************************************************************************
+ *
+ * Struct H5C2_class_t
+ *
+ * Instances of H5C2_class_t are used to specify the callback functions
+ * used by the metadata cache for each class of metadata cache entry.
+ * The fields of the structure are discussed below:
+ *
+ * id: Integer field containing the unique ID of the class of metadata
+ * cache entries.
+ *
+ * name: Pointer to a string containing the name of the class of metadata
+ * cache entries.
+ *
+ * mem_type: Instance of H5FD_mem_t, that is used to supply the
+ * mem type passed into H5F_block_read().
+ *
+ * deserialize: Pointer to the deserialize function.
+ *
+ * This function must be able to read an on disk image of a metadata
+ * cache entry, allocate and load the equivalent in core representation,
+ * and return a pointer to that representation.
+ *
+ * The typedef for the deserialize callback is as follows:
+ *
+ * typedef void *(*H5C_deserialize_func_t)(haddr_t addr,
+ * size_t len,
+ * const void * image_ptr,
+ * void * udata_ptr,
+ * boolean * dirty_ptr);
+ *
+ * The parameters of the deserialize callback are as follows:
+ *
+ * addr: Base address in file of the image to be deserialized.
+ *
+ * This parameter is supplied mainly for sanity checking.
+ * Sanity checks should be performed when compiled in debug
+ * mode, but the parameter may be unused when compiled in
+ * production mode.
+ *
+ * len: Length in bytes of the in file image to be deserialized.
+ *
+ * This parameter is supplied mainly for sanity checking.
+ * Sanity checks should be performed when compiled in debug
+ * mode, but the parameter may be unused when compiled in
+ * production mode.
+ *
+ * image_ptr: Pointer to a buffer of length len containing the
+ * contents of the file starting at addr and continuing
+ * for len bytes.
+ *
+ * udata_ptr: Pointer to user data provided in the protect call, which
+ * must be passed through to the deserialize callback.
+ *
+ * dirty_ptr: Pointer to boolean which the deserialize function
+ * must use to mark the entry dirty if it has to modify
+ * the entry to clean up file corruption left over from
+ * an old bug in the HDF5 library.
+ *
+ * Processing in the deserialize function should proceed as follows:
+ *
+ * If the image contains valid data, and is of the correct length,
+ * the deserialize function must allocate space for an in core
+ * represntation of that data, load the contents of the image into
+ * the space allocated for the in core representation, and return
+ * a pointer to the in core representation. Observe that an
+ * instance of H5C2_cache_entry_t must be the first item in this
+ * representation. It will have to be initialized appropriately
+ * after the callback returns.
+ *
+ * Note that the structure of the in core representation is otherwise
+ * up to the cache client. All that is required is that the pointer
+ * returned be sufficient for the clients purposes when it is returned
+ * on a protect call.
+ *
+ * If the deserialize function has to clean up file corruption
+ * left over from an old bug in the HDF5 library, it must set
+ * *dirty_ptr to TRUE. If it doesn't, no action is needed as
+ * *dirty_ptr will be set to FALSE before the deserialize call.
+ *
+ * If the operation fails for any reason (i.e. bad data in buffer, bad
+ * buffer length, malloc failure, etc.) the function must return NULL and
+ * push error information on the error stack with the error API routines.
+ *
+ * If the protect call which occasioned the call to the deserialize
+ * callback had the check length flag set, after the deserialize call
+ * returns, the cache must call the image_len callback (see below) and
+ * update its on disk image length accordingly.
+ *
+ *
+ * image_len: Pointer to the image length callback.
+ *
+ * In the best of all possible worlds, we would not have this callback.
+ * It exists to allow clients to reduce the size of the on disk image of
+ * an entry in the deserialize callback.
+ *
+ * The typedef for the image_len callback is as follows:
+ *
+ * typedef herr_t (*H5C_image_len_func_t)(void *thing,
+ * size_t *image_len_ptr);
+ *
+ * The parameters of the image_len callback are as follows:
+ *
+ * thing: Pointer to the in core representation of the entry.
+ *
+ * image_len_ptr: Pointer to size_t in which the callback will return
+ * the length of the on disk image of the cache entry.
+ *
+ * Processing in the image_len function should proceed as follows:
+ *
+ * If successful, the function will place the length of the on disk
+ * image associated with the in core representation provided in the
+ * thing parameter in *image_len_ptr, and then return SUCCEED.
+ *
+ * On failure, the function must return FAIL and push error information
+ * onto the error stack with the error API routines.
+ *
+ *
+ * serialize: Pointer to the serialize callback.
+ *
+ * The serialize callback is invoked by the metadata cache whenever
+ * it needs a current on disk image of the metadata entry for purposes
+ * either constructing a journal or flushing the entry to disk.
+ *
+ * At this point, one would think that the base address and length of
+ * the length of the entry's image on disk would be well known.
+ * However, that need not be the case as fractal heap blocks can
+ * change size (and therefor possible location as well) on
+ * serialization if compression is enabled. In the old H5C code,
+ * this happened on a flush, and occasioned a rename in the midst
+ * of the flush. To avoid this in H5C2, the serialize callback
+ * will return the new base address, length, and image pointer to
+ * the caller when necessary. The caller must then update the
+ * metadata cache's internal structures accordingly.
+ *
+ * The typedef for the serialize callback is as follows:
+ *
+ * typedef herr_t (*H5C_serialize_func_t)(haddr_t addr,
+ * size_t len,
+ * void * image_ptr,
+ * void * thing,
+ * unsigned * flags_ptr,
+ * haddr_t * new_addr_ptr,
+ * size_t * new_len_ptr,
+ * void ** new_image_ptr_ptr);
+ *
+ * The parameters of the serialize callback are as follows:
+ *
+ * addr: Base address in file of the entry to be serialized.
+ *
+ * This parameter is supplied mainly for sanity checking.
+ * Sanity checks should be performed when compiled in debug
+ * mode, but the parameter may be unused when compiled in
+ * production mode.
+ *
+ * len: Length in bytes of the in file image of the entry to be
+ * serialized. Also the size of *image_ptr (below).
+ *
+ * This parameter is supplied mainly for sanity checking.
+ * Sanity checks should be performed when compiled in debug
+ * mode, but the parameter may be unused when compiled in
+ * production mode.
+ *
+ * image_ptr: Pointer to a buffer of length len bytes into which a
+ * serialized image of the target metadata cache entry is
+ * to be written.
+ *
+ * Note that this buffer will not in general be initialized
+ * to any particular value. Thus the serialize function may
+ * not assume any initial value and must set each byte in
+ * the buffer.
+ *
+ * thing: Pointer to void containing the address of the in core
+ * representation of the target metadata cache entry.
+ * This is the same pointer returned by a protect of the
+ * addr and len given above.
+ *
+ * flags_ptr: Pointer to an unsigned integer used to return flags
+ * indicating whether the resize function resized or renamed
+ * the entry. If the entry was neither resized or renamed,
+ * the serialize function must set *flags_ptr to zero.
+ * H5C2__SERIALIZE_RESIZED_FLAG and H5C2__SERIALIZE_RENAMED_FLAG
+ * must be set to indicate a resize and a rename respectively.
+ *
+ * If the H5C2__SERIALIZE_RESIZED_FLAG is set, the new length
+ * and image pointer must be stored in *new_len_ptr and
+ * *new_image_ptr_ptr respectively.
+ *
+ * If the H5C2__SERIALIZE_RENAMED_FLAG flag is also set, the
+ * new image base address must be stored in *new_addr_ptr.
+ * Observe that the H5C2__SERIALIZE_RENAMED_FLAG must not
+ * appear without the H5C2__SERIALIZE_RESIZED_FLAG.
+ *
+ * Except as noted above, the locations pointed to by the
+ * remaining parameters are undefined, and should be ignored
+ * by the caller.
+ *
+ * new_addr_ptr: Pointer to haddr_t. If the entry is renamed by
+ * the serialize function, the new on disk base address must
+ * be stored in *new_addr_ptr. If the entry is not renamed
+ * by the serialize function, *new_addr_ptr is undefined.
+ *
+ * new_len_ptr: Pointer to size_t. If the entry is resized by the
+ * serialize function, the new length of the on disk image
+ * must be stored in *new_len_ptr. If the entry is not
+ * resized by the serialize function, *new_len_ptr is
+ * undefined.
+ *
+ * new_image_ptr_ptr: Pointer to pointer to void. If the entry is
+ * resized by the serialize function, the pointer to the
+ * new buffer containing the on disk image must be stored
+ * in *new_image_ptr_ptr. If the entry is not resized by
+ * the serialize function, *new_image_ptr_ptr is undefined.
+ *
+ * Processing in the serialize function should proceed as follows:
+ *
+ * The serialize function must examine the in core representation
+ * indicated by the thing parameter, and write a serialized image
+ * of its contents into the provided buffer.
+ *
+ * If the serialize function does not change the size or location
+ * of the on disk image, it must set *flags_ptr to zero.
+ *
+ * If the size of the on disk image must be changed, the serialize
+ * function must free the old image buffer (base address in image_ptr),
+ * allocate a new one, load the image into the new buffer, load the
+ * base address of the new buffer into *new_image_ptr_ptr, load the
+ * length of the new image into *new_len_ptr, and set the
+ * H5C2__SERIALIZE_RESIZED_FLAG in *flags_ptr.
+ *
+ * If in addition, the base address of the on disk image must
+ * be changed, the serialize function must also set *new_addr_ptr
+ * to the new base address, and set the H5C2__SERIALIZE_RENAMED_FLAG
+ * in *flags_ptr.
+ *
+ * If it is successful, the function must return SUCCEED.
+ *
+ * If it fails for any reason, the function must return FAIL and
+ * push error information on the error stack with the error API
+ * routines.
+ *
+ *
+ * free_icr: Pointer to the free ICR Callback.
+ *
+ * The free ICR callback is invoked by the metadata cache when it
+ * wishes to evict an entry, and needs the client to free the memory
+ * allocated for the in core representation.
+ *
+ * The typedef for the free ICR callback is as follows:
+ *
+ * typedef herr_t (*N5C_free_icr_func_t)(haddr_t addr,
+ * size_t len,
+ * void * thing);
+ *
+ * The parameters of the free ICR callback are as follows:
+ *
+ * addr: Base address in file of the entry being evicted.
+ *
+ * This parameter is supplied mainly for sanity checking.
+ * Sanity checks should be performed when compiled in debug
+ * mode, but the parameter may be unused when compiled in
+ * production mode.
+ *
+ * len: Length of the in file image of the entry being evicted
+ * in bytes.
+ *
+ * This parameter is supplied mainly for sanity checking.
+ * Sanity checks should be performed when compiled in debug
+ * mode, but the parameter may be unused when compiled in
+ * production mode.
+ *
+ * thing: Pointer to void containing the address of the in core
+ * representation of the target metadata cache entry. This
+ * is the same pointer that would be returned by a protect
+ * of the addr and len above.
+ *
+ * Processing in the free ICR function should proceed as follows:
+ *
+ * The free ICR function must free all memory allocated to the
+ * in core representation.
+ *
+ * If the function is successful, it must return SUCCEED.
+ *
+ * If it fails for any reason, the function must return FAIL and
+ * push error information on the error stack with the error API
+ * routines.
+ *
+ * At least when compiled with debug, it would be useful if the
+ * free ICR call would fail if the in core representation has been
+ * modified since the last serialize of clear callback.
+ *
+ *
+ * clear_dirty_bits: Pointer to the clear dirty bits callback.
+ *
+ * For sanity checking purposes, it will be useful if cache clients
+ * track whether an in core representation has been modified since
+ * the last time it was serialized. This data is used to flag an
+ * error if the cache attempts to free an in core representation
+ * that has not been serialized since the last time it was modified.
+ *
+ * If this happens, either the client forgot to tell the cache that
+ * an entry is dirty, or the cache forgot to flush a dirty entry
+ * before evicting it. In either case we want to know before we
+ * get file corruption complaints.
+ *
+ * However, in some cases, we want to mark an entry as clean even
+ * though it has not been flushed to disk -- most particularly in
+ * the parallel case. Thus we need some way to tell the client
+ * that a free of the associated ICR is OK even though it has
+ * been modified since the last serialization. Hence the clear
+ * dirty bits callback.
+ *
+ * Since the clear dirty bits callback is purely for sanity checking,
+ * it is called only when we compile with debug.
+ *
+ * The typedef for the clear callback is as follows:
+ *
+ * typedef herr_t (*N5C_clear_dirty_bits_func_t)(haddr_t addr,
+ * size_t len,
+ * void * thing);
+ *
+ * The parameters of the clear callback are as follows:
+ *
+ * addr: Base address in file of the entry whose dirty bits
+ * are being cleared
+ *
+ * len: Length in bytes of the in file image of the entry
+ * whose dirty bits are being cleared.
+ *
+ * thing: Pointer to void containing the address of the in
+ * core representation of the target metadata cache entry.
+ * This is the same pointer that would be returned by a
+ * protect of the addr and len above.
+ *
+ * Processing in the clear callback function should proceed as follows:
+ *
+ * The function must clear any dirty bits associated with the ICR.
+ *
+ * If successful, the function must return SUCCEED.
+ *
+ * If it fails for any reason, the function must return FAIL and
+ * push error information on the error stack with the error API
+ * routines.
+ *
+ ***************************************************************************/
+typedef void *(*H5C2_deserialize_func_t)(haddr_t addr,
+ size_t len,
+ const void * image_ptr,
+ const void * udata_ptr,
+ hbool_t * dirty_ptr);
+
+typedef herr_t (*H5C2_image_len_func_t)(void *thing,
+ size_t *image_len_ptr);
+
+#define H5C2__SERIALIZE_RESIZED_FLAG 0x1
+#define H5C2__SERIALIZE_RENAMED_FLAG 0x2
+
+typedef herr_t (*H5C2_serialize_func_t)(haddr_t addr,
+ size_t len,
+ void * image_ptr,
+ void * thing,
+ unsigned * flags_ptr,
+ haddr_t * new_addr_ptr,
+ size_t * new_len_ptr,
+ void ** new_image_ptr_ptr);
+
+typedef herr_t (*H5C2_free_icr_func_t)(haddr_t addr,
+ size_t len,
+ void * thing);
+
+typedef herr_t (*H5C2_clear_dirty_bits_func_t)(haddr_t addr,
+ size_t len,
+ void * thing);
+
+typedef struct H5C2_class_t {
+ int id;
+ const char * name;
+ H5FD_mem_t mem_type;
+ H5C2_deserialize_func_t deserialize;
+ H5C2_image_len_func_t image_len;
+ H5C2_serialize_func_t serialize;
+ H5C2_free_icr_func_t free_icr;
+ H5C2_clear_dirty_bits_func_t clear_dirty_bits;
+} H5C2_class_t;
+
+
+/* Type defintions of call back functions used by the cache as a whole */
+
+typedef herr_t (*H5C2_write_permitted_func_t)(const H5F_t *f,
+ hid_t dxpl_id,
+ hbool_t * write_permitted_ptr);
+
+typedef herr_t (*H5C2_log_flush_func_t)(H5C2_t * cache_ptr,
+ haddr_t addr,
+ hbool_t was_dirty,
+ unsigned flags,
+ int type_id);
+
+/* Upper and lower limits on cache size. These limits are picked
+ * out of a hat -- you should be able to change them as necessary.
+ *
+ * However, if you need a very big cache, you should also increase the
+ * size of the hash table (H5C2__HASH_TABLE_LEN in H5C2pkg.h). The current
+ * upper bound on cache size is rather large for the current hash table
+ * size.
+ */
+
+#define H5C2__MAX_MAX_CACHE_SIZE ((size_t)(128 * 1024 * 1024))
+#define H5C2__MIN_MAX_CACHE_SIZE ((size_t)(1024))
+
+
+/* Default max cache size and min clean size are give here to make
+ * them generally accessable.
+ */
+
+#define H5C2__DEFAULT_MAX_CACHE_SIZE ((size_t)(4 * 1024 * 1024))
+#define H5C2__DEFAULT_MIN_CLEAN_SIZE ((size_t)(2 * 1024 * 1024))
+
+
+/****************************************************************************
+ *
+ * structure H5C2_cache_entry_t
+ *
+ * Instances of the H5C2_cache_entry_t structure are used to store cache
+ * entries in a hash table and sometimes in a skip list.
+ * See H5SL.c for the particulars of the skip list.
+ *
+ * In typical application, this structure is the first field in a
+ * structure to be cached. For historical reasons, the external module
+ * is responsible for managing the is_dirty field (this is no longer
+ * completely true. See the comment on the is_dirty field for details).
+ * All other fields are managed by the cache.
+ *
+ * The fields of this structure are discussed individually below:
+ *
+ * JRM - 4/26/04
+ *
+ * magic: Unsigned 32 bit integer that must always be set to
+ * H5C2__H5C2_CACHE_ENTRY_T_MAGIC when the entry is valid.
+ * The field must be set to H5C2__H5C2_CACHE_ENTRY_T_BAD_MAGIC
+ * just before the entry is freed.
+ *
+ * This is necessary, as the LRU list can be changed out
+ * from under H5C2_make_space_in_cache() by the serialize
+ * callback which may change the size of an existing entry,
+ * and/or load a new entry while serializing the target entry.
+ *
+ * This in turn can cause a recursive call to
+ * H5C2_make_space_in_cache() which may either flush or evict
+ * the next entry that the first invocation of that function
+ * was about to examine.
+ *
+ * The magic field allows H5C2_make_space_in_cache() to
+ * detect this case, and re-start its scan from the bottom
+ * of the LRU when this situation occurs.
+ *
+ * addr: Base address of the cache entry on disk.
+ *
+ * size: Length of the cache entry on disk. Note that unlike normal
+ * caches, the entries in this cache are of variable length.
+ * The entries should never overlap, and when we do writebacks,
+ * we will want to writeback adjacent entries where possible.
+ *
+ * NB: At present, entries need not be contiguous on disk. Until
+ * we fix this, we can't do much with writing back adjacent
+ * entries.
+ *
+ * Update: This has now been changed -- all metadata cache
+ * entries must now be associated with a single contiguous
+ * block of memory on disk. The image of this block (i.e.
+ * the on disk image) is stored in *image_ptr (discussed below).
+ *
+ * image_ptr: Pointer to void. When not NULL, this field points to a
+ * dynamically allocated block of size bytes in which the
+ * on disk image of the metadata cache entry is stored.
+ *
+ * If the entry is dirty, the serialize callback must be used
+ * to update this image before it is written to disk
+ *
+ * type: Pointer to the instance of H5C2_class_t containing pointers
+ * to the methods for cache entries of the current type. This
+ * field should be NULL when the instance of H5C2_cache_entry_t
+ * is not in use.
+ *
+ * The name is not particularly descriptive, but is retained
+ * to avoid changes in existing code.
+ *
+ * is_dirty: Boolean flag indicating whether the contents of the cache
+ * entry has been modified since the last time it was written
+ * to disk.
+ *
+ * NOTE: For historical reasons, this field is not maintained
+ * by the cache. Instead, the module using the cache
+ * sets this flag when it modifies the entry, and the
+ * flush and clear functions supplied by that module
+ * reset the dirty when appropriate.
+ *
+ * This is a bit quirky, so we may want to change this
+ * someday. However it will require a change in the
+ * cache interface.
+ *
+ * Update: Management of the is_dirty field has been largely
+ * moved into the cache. The only remaining exceptions
+ * are the flush and clear functions supplied by the
+ * modules using the cache. These still clear the
+ * is_dirty field as before. -- JRM 7/5/05
+ *
+ * Update: Management of the is_dirty field is now entirely
+ * in the cache. -- JRM 7/5/07
+ *
+ * dirtied: Boolean flag used to indicate that the entry has been
+ * dirtied while protected.
+ *
+ * This field is set to FALSE in the protect call, and may
+ * be set to TRUE by the
+ * H5C2_mark_pinned_or_protected_entry_dirty()
+ * call at an time prior to the unprotect call.
+ *
+ * The H5C2_mark_pinned_or_protected_entry_dirty() call exists
+ * as a convenience function for the fractal heap code which
+ * may not know if an entry is protected or pinned, but knows
+ * that is either protected or pinned. The dirtied field was
+ * added as in the parallel case, it is necessary to know
+ * whether a protected entry was dirty prior to the protect call.
+ *
+ * is_protected: Boolean flag indicating whether this entry is protected
+ * (or locked, to use more conventional terms). When it is
+ * protected, the entry cannot be flushed or accessed until
+ * it is unprotected (or unlocked -- again to use more
+ * conventional terms).
+ *
+ * Note that protected entries are removed from the LRU lists
+ * and inserted on the protected list.
+ *
+ * is_read_only: Boolean flag that is only meaningful if is_protected is
+ * TRUE. In this circumstance, it indicates whether the
+ * entry has been protected read only, or read/write.
+ *
+ * If the entry has been protected read only (i.e. is_protected
+ * and is_read_only are both TRUE), we allow the entry to be
+ * protected more than once.
+ *
+ * In this case, the number of readers is maintained in the
+ * ro_ref_count field (see below), and unprotect calls simply
+ * decrement that field until it drops to zero, at which point
+ * the entry is actually unprotected.
+ *
+ * ro_ref_count: Integer field used to maintain a count of the number of
+ * outstanding read only protects on this entry. This field
+ * must be zero whenever either is_protected or is_read_only
+ * are TRUE.
+ *
+ * is_pinned: Boolean flag indicating whether the entry has been pinned
+ * in the cache.
+ *
+ * For very hot entries, the protect / unprotect overhead
+ * can become excessive. Thus the cache has been extended
+ * to allow an entry to be "pinned" in the cache.
+ *
+ * Pinning an entry in the cache has several implications:
+ *
+ * 1) A pinned entry cannot be evicted. Thus unprotected
+ * pinned entries must be stored in the pinned entry
+ * list, instead of being managed by the replacement
+ * policy code (LRU at present).
+ *
+ * 2) A pinned entry can be accessed or modified at any time.
+ * Therefore, the cache must check with the entry owner
+ * before flushing it. If permission is denied, the
+ * cache does not flush the entry.
+ *
+ * 3) A pinned entry can be marked as dirty (and possibly
+ * change size) while it is unprotected.
+ *
+ * 4) The flush-destroy code must allow pinned entries to
+ * be unpinned (and possibly unprotected) during the
+ * flush.
+ *
+ * JRM -- 3/16/06
+ *
+ * in_slist: Boolean flag indicating whether the entry is in the skip list
+ * As a general rule, entries are placed in the list when they
+ * are marked dirty. However they may remain in the list after
+ * being flushed.
+ *
+ * Update: Dirty entries are now removed from the skip list
+ * when they are flushed.
+ *
+ * flush_marker: Boolean flag indicating that the entry is to be flushed
+ * the next time H5C2_flush_cache() is called with the
+ * H5AC__FLUSH_MARKED_ENTRIES_FLAG. The flag is reset when
+ * the entry is flushed for whatever reason.
+ *
+ * clear_on_unprotect: Boolean flag used only in PHDF5. When H5C2 is used
+ * to implement the metadata cache In the parallel case, only
+ * the cache with mpi rank 0 is allowed to actually write to
+ * file -- all other caches must retain dirty entries until they
+ * are advised that the entry is clean.
+ *
+ * This flag is used in the case that such an advisory is
+ * received when the entry is protected. If it is set when an
+ * entry is unprotected, and the dirtied flag is not set in
+ * the unprotect, the entry's is_dirty flag is reset by flushing
+ * it with the H5C2__FLUSH_CLEAR_ONLY_FLAG.
+ *
+ * flush_in_progress: Boolean flag that is set to true iff the entry
+ * is in the process of being flushed. This allows the cache
+ * to detect when a call is the result of a flush callback.
+ *
+ * destroy_in_progress: Boolean flag that is set to true iff the entry
+ * is in the process of being flushed and destroyed.
+ *
+ *
+ * Fields supporting the hash table:
+ *
+ * Fields in the cache are indexed by a more or less conventional hash table.
+ * If there are multiple entries in any hash bin, they are stored in a doubly
+ * linked list.
+ *
+ * ht_next: Next pointer used by the hash table to store multiple
+ * entries in a single hash bin. This field points to the
+ * next entry in the doubly linked list of entries in the
+ * hash bin, or NULL if there is no next entry.
+ *
+ * ht_prev: Prev pointer used by the hash table to store multiple
+ * entries in a single hash bin. This field points to the
+ * previous entry in the doubly linked list of entries in
+ * the hash bin, or NULL if there is no previuos entry.
+ *
+ *
+ * Fields supporting replacement policies:
+ *
+ * The cache must have a replacement policy, and it will usually be
+ * necessary for this structure to contain fields supporting that policy.
+ *
+ * While there has been interest in several replacement policies for
+ * this cache, the initial development schedule is tight. Thus I have
+ * elected to support only a modified LRU policy for the first cut.
+ *
+ * When additional replacement policies are added, the fields in this
+ * section will be used in different ways or not at all. Thus the
+ * documentation of these fields is repeated for each replacement policy.
+ *
+ * Modified LRU:
+ *
+ * When operating in parallel mode, we must ensure that a read does not
+ * cause a write. If it does, the process will hang, as the write will
+ * be collective and the other processes will not know to participate.
+ *
+ * To deal with this issue, I have modified the usual LRU policy by adding
+ * clean and dirty LRU lists to the usual LRU list. When reading in
+ * parallel mode, we evict from the clean LRU list only. This implies
+ * that we must try to ensure that the clean LRU list is reasonably well
+ * stocked. See the comments on H5C2_t in H5C2pkg.h for more details.
+ *
+ * Note that even if we start with a completely clean cache, a sequence
+ * of protects without unprotects can empty the clean LRU list. In this
+ * case, the cache must grow temporarily. At the next write, we will
+ * attempt to evict enough entries to get the cache down to its nominal
+ * maximum size.
+ *
+ * The use of the replacement policy fields under the Modified LRU policy
+ * is discussed below:
+ *
+ * next: Next pointer in either the LRU or the protected list,
+ * depending on the current value of protected. If there
+ * is no next entry on the list, this field should be set
+ * to NULL.
+ *
+ * prev: Prev pointer in either the LRU or the protected list,
+ * depending on the current value of protected. If there
+ * is no previous entry on the list, this field should be
+ * set to NULL.
+ *
+ * aux_next: Next pointer on either the clean or dirty LRU lists.
+ * This entry should be NULL when protected is true. When
+ * protected is false, and dirty is true, it should point
+ * to the next item on the dirty LRU list. When protected
+ * is false, and dirty is false, it should point to the
+ * next item on the clean LRU list. In either case, when
+ * there is no next item, it should be NULL.
+ *
+ * aux_prev: Previous pointer on either the clean or dirty LRU lists.
+ * This entry should be NULL when protected is true. When
+ * protected is false, and dirty is true, it should point
+ * to the previous item on the dirty LRU list. When protected
+ * is false, and dirty is false, it should point to the
+ * previous item on the clean LRU list. In either case, when
+ * there is no previous item, it should be NULL.
+ *
+ *
+ * Cache entry stats collection fields:
+ *
+ * These fields should only be compiled in when both H5C2_COLLECT_CACHE_STATS
+ * and H5C2_COLLECT_CACHE_ENTRY_STATS are true. When present, they allow
+ * collection of statistics on individual cache entries.
+ *
+ * accesses: int32_t containing the number of times this cache entry has
+ * been referenced in its lifetime.
+ *
+ * clears: int32_t containing the number of times this cache entry has
+ * been cleared in its life time.
+ *
+ * flushes: int32_t containing the number of times this cache entry has
+ * been flushed to file in its life time.
+ *
+ * pins: int32_t containing the number of times this cache entry has
+ * been pinned in cache in its life time.
+ *
+ ****************************************************************************/
+
+#define H5C2__H5C2_CACHE_ENTRY_T_MAGIC 0x005CAC0A
+#define H5C2__H5C2_CACHE_ENTRY_T_BAD_MAGIC 0xDeadBeef
+
+typedef struct H5C2_cache_entry_t
+{
+ uint32_t magic;
+ haddr_t addr;
+ size_t size;
+ void * image_ptr;
+ const H5C2_class_t * type;
+ hbool_t is_dirty;
+ hbool_t dirtied;
+ hbool_t is_protected;
+ hbool_t is_read_only;
+ int ro_ref_count;
+ hbool_t is_pinned;
+ hbool_t in_slist;
+ hbool_t flush_marker;
+#ifdef H5_HAVE_PARALLEL
+ hbool_t clear_on_unprotect;
+#endif /* H5_HAVE_PARALLEL */
+ hbool_t flush_in_progress;
+ hbool_t destroy_in_progress;
+
+ /* fields supporting the hash table: */
+
+ struct H5C2_cache_entry_t * ht_next;
+ struct H5C2_cache_entry_t * ht_prev;
+
+ /* fields supporting replacement policies: */
+
+ struct H5C2_cache_entry_t * next;
+ struct H5C2_cache_entry_t * prev;
+ struct H5C2_cache_entry_t * aux_next;
+ struct H5C2_cache_entry_t * aux_prev;
+
+#if H5C2_COLLECT_CACHE_ENTRY_STATS
+
+ /* cache entry stats fields */
+
+ int32_t accesses;
+ int32_t clears;
+ int32_t flushes;
+ int32_t pins;
+
+#endif /* H5C2_COLLECT_CACHE_ENTRY_STATS */
+
+} H5C2_cache_entry_t;
+
+
+/****************************************************************************
+ *
+ * structure H5C2_auto_size_ctl_t
+ *
+ * Instances of H5C2_auto_size_ctl_t are used to get and set the control
+ * fields for automatic cache re-sizing.
+ *
+ * The fields of the structure are discussed individually below:
+ *
+ * version: Integer field containing the version number of this version
+ * of the H5C2_auto_size_ctl_t structure. Any instance of
+ * H5C2_auto_size_ctl_t passed to the cache must have a known
+ * version number, or an error will be flagged.
+ *
+ * report_fcn: Pointer to the function that is to be called to report
+ * activities each time the auto cache resize code is executed. If the
+ * field is NULL, no call is made.
+ *
+ * If the field is not NULL, it must contain the address of a function
+ * of type H5C2_auto_resize_report_fcn.
+ *
+ * set_initial_size: Boolean flag indicating whether the size of the
+ * initial size of the cache is to be set to the value given in
+ * the initial_size field. If set_initial_size is FALSE, the
+ * initial_size field is ignored.
+ *
+ * initial_size: If enabled, this field contain the size the cache is
+ * to be set to upon receipt of this structure. Needless to say,
+ * initial_size must lie in the closed interval [min_size, max_size].
+ *
+ * min_clean_fraction: double in the range 0 to 1 indicating the fraction
+ * of the cache that is to be kept clean. This field is only used
+ * in parallel mode. Typical values are 0.1 to 0.5.
+ *
+ * max_size: Maximum size to which the cache can be adjusted. The
+ * supplied value must fall in the closed interval
+ * [MIN_MAX_CACHE_SIZE, MAX_MAX_CACHE_SIZE]. Also, max_size must
+ * be greater than or equal to min_size.
+ *
+ * min_size: Minimum size to which the cache can be adjusted. The
+ * supplied value must fall in the closed interval
+ * [MIN_MAX_CACHE_SIZE, MAX_MAX_CACHE_SIZE]. Also, min_size must
+ * be less than or equal to max_size.
+ *
+ * epoch_length: Number of accesses on the cache over which to collect
+ * hit rate stats before running the automatic cache resize code,
+ * if it is enabled.
+ *
+ * At the end of an epoch, we discard prior hit rate data and start
+ * collecting afresh. The epoch_length must lie in the closed
+ * interval [H5C2__MIN_AR_EPOCH_LENGTH, H5C2__MAX_AR_EPOCH_LENGTH].
+ *
+ *
+ * Cache size increase control fields:
+ *
+ * incr_mode: Instance of the H5C2_cache_incr_mode enumerated type whose
+ * value indicates how we determine whether the cache size should be
+ * increased. At present there are two possible values:
+ *
+ * H5C2_incr__off: Don't attempt to increase the size of the cache
+ * automatically.
+ *
+ * When this increment mode is selected, the remaining fields
+ * in the cache size increase section ar ignored.
+ *
+ * H5C2_incr__threshold: Attempt to increase the size of the cache
+ * whenever the average hit rate over the last epoch drops
+ * below the value supplied in the lower_hr_threshold
+ * field.
+ *
+ * Note that this attempt will fail if the cache is already
+ * at its maximum size, or if the cache is not already using
+ * all available space.
+ *
+ * lower_hr_threshold: Lower hit rate threshold. If the increment mode
+ * (incr_mode) is H5C2_incr__threshold and the hit rate drops below the
+ * value supplied in this field in an epoch, increment the cache size by
+ * size_increment. Note that cache size may not be incremented above
+ * max_size, and that the increment may be further restricted by the
+ * max_increment field if it is enabled.
+ *
+ * When enabled, this field must contain a value in the range [0.0, 1.0].
+ * Depending on the incr_mode selected, it may also have to be less than
+ * upper_hr_threshold.
+ *
+ * increment: Double containing the multiplier used to derive the new
+ * cache size from the old if a cache size increment is triggered.
+ * The increment must be greater than 1.0, and should not exceed 2.0.
+ *
+ * The new cache size is obtained by multiplying the current max cache
+ * size by the increment, and then clamping to max_size and to stay
+ * within the max_increment as necessary.
+ *
+ * apply_max_increment: Boolean flag indicating whether the max_increment
+ * field should be used to limit the maximum cache size increment.
+ *
+ * max_increment: If enabled by the apply_max_increment field described
+ * above, this field contains the maximum number of bytes by which the
+ * cache size can be increased in a single re-size.
+ *
+ *
+ * Cache size decrease control fields:
+ *
+ * decr_mode: Instance of the H5C2_cache_decr_mode enumerated type whose
+ * value indicates how we determine whether the cache size should be
+ * decreased. At present there are four possibilities.
+ *
+ * H5C2_decr__off: Don't attempt to decrease the size of the cache
+ * automatically.
+ *
+ * When this increment mode is selected, the remaining fields
+ * in the cache size decrease section are ignored.
+ *
+ * H5C2_decr__threshold: Attempt to decrease the size of the cache
+ * whenever the average hit rate over the last epoch rises
+ * above the value supplied in the upper_hr_threshold
+ * field.
+ *
+ * H5C2_decr__age_out: At the end of each epoch, search the cache for
+ * entries that have not been accessed for at least the number
+ * of epochs specified in the epochs_before_eviction field, and
+ * evict these entries. Conceptually, the maximum cache size
+ * is then decreased to match the new actual cache size. However,
+ * this reduction may be modified by the min_size, the
+ * max_decrement, and/or the empty_reserve.
+ *
+ * H5C2_decr__age_out_with_threshold: Same as age_out, but we only
+ * attempt to reduce the cache size when the hit rate observed
+ * over the last epoch exceeds the value provided in the
+ * upper_hr_threshold field.
+ *
+ * upper_hr_threshold: Upper hit rate threshold. The use of this field
+ * varies according to the current decr_mode:
+ *
+ * H5C2_decr__off or H5C2_decr__age_out: The value of this field is
+ * ignored.
+ *
+ * H5C2_decr__threshold: If the hit rate exceeds this threshold in any
+ * epoch, attempt to decrement the cache size by size_decrement.
+ *
+ * Note that cache size may not be decremented below min_size.
+ *
+ * Note also that if the upper_threshold is 1.0, the cache size
+ * will never be reduced.
+ *
+ * H5C2_decr__age_out_with_threshold: If the hit rate exceeds this
+ * threshold in any epoch, attempt to reduce the cache size
+ * by evicting entries that have not been accessed for more
+ * than the specified number of epochs.
+ *
+ * decrement: This field is only used when the decr_mode is
+ * H5C2_decr__threshold.
+ *
+ * The field is a double containing the multiplier used to derive the
+ * new cache size from the old if a cache size decrement is triggered.
+ * The decrement must be in the range 0.0 (in which case the cache will
+ * try to contract to its minimum size) to 1.0 (in which case the
+ * cache will never shrink).
+ *
+ * apply_max_decrement: Boolean flag used to determine whether decrements
+ * in cache size are to be limited by the max_decrement field.
+ *
+ * max_decrement: Maximum number of bytes by which the cache size can be
+ * decreased in a single re-size. Note that decrements may also be
+ * restricted by the min_size of the cache, and (in age out modes) by
+ * the empty_reserve field.
+ *
+ * epochs_before_eviction: Integer field used in H5C2_decr__age_out and
+ * H5C2_decr__age_out_with_threshold decrement modes.
+ *
+ * This field contains the number of epochs an entry must remain
+ * unaccessed before it is evicted in an attempt to reduce the
+ * cache size. If applicable, this field must lie in the range
+ * [1, H5C2__MAX_EPOCH_MARKERS].
+ *
+ * apply_empty_reserve: Boolean field controlling whether the empty_reserve
+ * field is to be used in computing the new cache size when the
+ * decr_mode is H5C2_decr__age_out or H5C2_decr__age_out_with_threshold.
+ *
+ * empty_reserve: To avoid a constant racheting down of cache size by small
+ * amounts in the H5C2_decr__age_out and H5C2_decr__age_out_with_threshold
+ * modes, this field allows one to require that any cache size
+ * reductions leave the specified fraction of unused space in the cache.
+ *
+ * The value of this field must be in the range [0.0, 1.0]. I would
+ * expect typical values to be in the range of 0.01 to 0.1.
+ *
+ ****************************************************************************/
+
+#define H5C2_RESIZE_CFG__VALIDATE_GENERAL 0x1
+#define H5C2_RESIZE_CFG__VALIDATE_INCREMENT 0x2
+#define H5C2_RESIZE_CFG__VALIDATE_DECREMENT 0x4
+#define H5C2_RESIZE_CFG__VALIDATE_INTERACTIONS 0x8
+#define H5C2_RESIZE_CFG__VALIDATE_ALL \
+( \
+ H5C2_RESIZE_CFG__VALIDATE_GENERAL | \
+ H5C2_RESIZE_CFG__VALIDATE_INCREMENT | \
+ H5C2_RESIZE_CFG__VALIDATE_DECREMENT | \
+ H5C2_RESIZE_CFG__VALIDATE_INTERACTIONS \
+)
+
+#define H5C2__CURR_AUTO_SIZE_CTL_VER 1
+#define H5C2__CURR_AUTO_RESIZE_RPT_FCN_VER 1
+
+#define H5C2__MAX_EPOCH_MARKERS 10
+
+#define H5C2__DEF_AR_UPPER_THRESHHOLD 0.9999
+#define H5C2__DEF_AR_LOWER_THRESHHOLD 0.9
+#define H5C2__DEF_AR_MAX_SIZE ((size_t)(16 * 1024 * 1024))
+#define H5C2__DEF_AR_INIT_SIZE ((size_t)( 1 * 1024 * 1024))
+#define H5C2__DEF_AR_MIN_SIZE ((size_t)( 1 * 1024 * 1024))
+#define H5C2__DEF_AR_MIN_CLEAN_FRAC 0.5
+#define H5C2__DEF_AR_INCREMENT 2.0
+#define H5C2__DEF_AR_MAX_INCREMENT ((size_t)( 2 * 1024 * 1024))
+#define H5C2__DEF_AR_DECREMENT 0.9
+#define H5C2__DEF_AR_MAX_DECREMENT ((size_t)( 1 * 1024 * 1024))
+#define H5C2__DEF_AR_EPCHS_B4_EVICT 3
+#define H5C2__DEF_AR_EMPTY_RESERVE 0.05
+#define H5C2__MIN_AR_EPOCH_LENGTH 100
+#define H5C2__DEF_AR_EPOCH_LENGTH 50000
+#define H5C2__MAX_AR_EPOCH_LENGTH 1000000
+
+enum H5C2_resize_status
+{
+ in_spec2,
+ increase2,
+ decrease2,
+ at_max_size2,
+ at_min_size2,
+ increase_disabled2,
+ decrease_disabled2,
+ not_full2
+}; /* enum H5C2_resize_conditions */
+
+typedef void (*H5C2_auto_resize_rpt_fcn)(H5C2_t * cache_ptr,
+ int32_t version,
+ double hit_rate,
+ enum H5C2_resize_status status,
+ size_t old_max_cache_size,
+ size_t new_max_cache_size,
+ size_t old_min_clean_size,
+ size_t new_min_clean_size);
+
+typedef struct H5C2_auto_size_ctl_t
+{
+ /* general configuration fields: */
+ int32_t version;
+ H5C2_auto_resize_rpt_fcn rpt_fcn;
+
+ hbool_t set_initial_size;
+ size_t initial_size;
+
+ double min_clean_fraction;
+
+ size_t max_size;
+ size_t min_size;
+
+ int64_t epoch_length;
+
+
+ /* size increase control fields: */
+ enum H5C2_cache_incr_mode incr_mode;
+
+ double lower_hr_threshold;
+
+ double increment;
+
+ hbool_t apply_max_increment;
+ size_t max_increment;
+
+
+ /* size decrease control fields: */
+ enum H5C2_cache_decr_mode decr_mode;
+
+ double upper_hr_threshold;
+
+ double decrement;
+
+ hbool_t apply_max_decrement;
+ size_t max_decrement;
+
+ int32_t epochs_before_eviction;
+
+ hbool_t apply_empty_reserve;
+ double empty_reserve;
+
+} H5C2_auto_size_ctl_t;
+
+
+/*
+ * Library prototypes.
+ */
+
+/* #defines of flags used in the flags parameters in some of the
+ * following function calls. Note that not all flags are applicable
+ * to all function calls. Flags that don't apply to a particular
+ * function are ignored in that function.
+ *
+ * These flags apply to all function calls:
+ *
+ * H5C2__NO_FLAGS_SET (generic "no flags set" for all fcn calls)
+ *
+ *
+ * These flags apply to H5C2_insert_entry():
+ *
+ * H5C2__SET_FLUSH_MARKER_FLAG
+ * H5C2__PIN_ENTRY_FLAG
+ *
+ * These flags apply to H5C2_protect()
+ *
+ * H5C2__READ_ONLY_FLAG
+ * H5C2__CHECK_SIZE_FLAG
+ *
+ * These flags apply to H5C2_unprotect():
+ *
+ * H5C2__SET_FLUSH_MARKER_FLAG
+ * H5C2__DELETED_FLAG
+ * H5C2__DIRTIED_FLAG
+ * H5C2__SIZE_CHANGED_FLAG
+ * H5C2__PIN_ENTRY_FLAG
+ * H5C2__UNPIN_ENTRY_FLAG
+ *
+ *
+ * These flags apply to H5C2_flush_cache():
+ *
+ * H5C2__FLUSH_INVALIDATE_FLAG
+ * H5C2__FLUSH_CLEAR_ONLY_FLAG
+ * H5C2__FLUSH_MARKED_ENTRIES_FLAG
+ * H5C2__FLUSH_IGNORE_PROTECTED_FLAG (can't use this flag in combination
+ * with H5C2__FLUSH_INVALIDATE_FLAG)
+ *
+ * These flags apply to H5C2_flush_single_entry():
+ *
+ * H5C2__FLUSH_INVALIDATE_FLAG
+ * H5C2__FLUSH_CLEAR_ONLY_FLAG
+ * H5C2__FLUSH_MARKED_ENTRIES_FLAG
+ */
+
+#define H5C2__NO_FLAGS_SET 0x0000
+#define H5C2__SET_FLUSH_MARKER_FLAG 0x0001
+#define H5C2__DELETED_FLAG 0x0002
+#define H5C2__DIRTIED_FLAG 0x0004
+#define H5C2__SIZE_CHANGED_FLAG 0x0008
+#define H5C2__PIN_ENTRY_FLAG 0x0010
+#define H5C2__UNPIN_ENTRY_FLAG 0x0020
+#define H5C2__FLUSH_INVALIDATE_FLAG 0x0040
+#define H5C2__FLUSH_CLEAR_ONLY_FLAG 0x0080
+#define H5C2__FLUSH_MARKED_ENTRIES_FLAG 0x0100
+#define H5C2__FLUSH_IGNORE_PROTECTED_FLAG 0x0200
+#define H5C2__READ_ONLY_FLAG 0x0400
+#define H5C2__CHECK_SIZE_FLAG 0x0800
+
+H5_DLL H5C2_t * H5C2_create(const H5F_t * f,
+ size_t max_cache_size,
+ size_t min_clean_size,
+ int max_type_id,
+ const char * (* type_name_table_ptr),
+ H5C2_write_permitted_func_t check_write_permitted,
+ hbool_t write_permitted,
+ H5C2_log_flush_func_t log_flush,
+ void * aux_ptr);
+
+H5_DLL void H5C2_def_auto_resize_rpt_fcn(H5C2_t * cache_ptr,
+ int32_t version,
+ double hit_rate,
+ enum H5C2_resize_status status,
+ size_t old_max_cache_size,
+ size_t new_max_cache_size,
+ size_t old_min_clean_size,
+ size_t new_min_clean_size);
+
+H5_DLL herr_t H5C2_dest(H5C2_t * cache_ptr,
+ hid_t dxpl_id);
+
+H5_DLL herr_t H5C2_dest_empty(H5C2_t * cache_ptr);
+
+H5_DLL herr_t H5C2_expunge_entry(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ const H5C2_class_t * type,
+ haddr_t addr);
+
+H5_DLL herr_t H5C2_flush_cache(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ unsigned flags);
+
+
+H5_DLL herr_t H5C2_flush_to_min_clean(H5C2_t * cache_ptr,
+ hid_t dxpl_id);
+
+H5_DLL herr_t H5C2_get_cache_auto_resize_config(H5C2_t * cache_ptr,
+ H5C2_auto_size_ctl_t *config_ptr);
+
+H5_DLL herr_t H5C2_get_cache_size(H5C2_t * cache_ptr,
+ size_t * max_size_ptr,
+ size_t * min_clean_size_ptr,
+ size_t * cur_size_ptr,
+ int32_t * cur_num_entries_ptr);
+
+H5_DLL herr_t H5C2_get_cache_hit_rate(H5C2_t * cache_ptr,
+ double * hit_rate_ptr);
+
+H5_DLL herr_t H5C2_get_entry_status(H5C2_t * cache_ptr,
+ haddr_t addr,
+ size_t * size_ptr,
+ hbool_t * in_cache_ptr,
+ hbool_t * is_dirty_ptr,
+ hbool_t * is_protected_ptr,
+ hbool_t * is_pinned_ptr);
+
+H5_DLL herr_t H5C2_get_evictions_enabled(H5C2_t * cache_ptr,
+ hbool_t * evictions_enabled_ptr);
+
+H5_DLL herr_t H5C2_get_trace_file_ptr(H5C2_t * cache_ptr,
+ FILE ** trace_file_ptr_ptr);
+
+H5_DLL herr_t H5C2_insert_entry(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ const H5C2_class_t * type,
+ haddr_t addr,
+ size_t len,
+ void * thing,
+ unsigned int flags);
+
+H5_DLL herr_t H5C2_mark_entries_as_clean(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ int32_t ce_array_len,
+ haddr_t * ce_array_ptr);
+
+H5_DLL herr_t H5C2_mark_pinned_entry_dirty(H5C2_t * cache_ptr,
+ void * thing,
+ hbool_t size_changed,
+ size_t new_size);
+
+H5_DLL herr_t H5C2_mark_pinned_or_protected_entry_dirty(H5C2_t * cache_ptr,
+ void * thing);
+
+H5_DLL herr_t H5C2_rename_entry(H5C2_t * cache_ptr,
+ const H5C2_class_t * type,
+ haddr_t old_addr,
+ haddr_t new_addr);
+
+H5_DLL herr_t H5C2_pin_protected_entry(H5C2_t * cache_ptr,
+ void * thing);
+
+H5_DLL void * H5C2_protect(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ const H5C2_class_t * type,
+ haddr_t addr,
+ size_t len,
+ const void * udata,
+ unsigned flags);
+
+H5_DLL herr_t H5C2_reset_cache_hit_rate_stats(H5C2_t * cache_ptr);
+
+H5_DLL herr_t H5C2_resize_pinned_entry(H5C2_t * cache_ptr,
+ void * thing,
+ size_t new_size);
+
+H5_DLL herr_t H5C2_set_cache_auto_resize_config(H5C2_t * cache_ptr,
+ H5C2_auto_size_ctl_t *config_ptr);
+
+H5_DLL herr_t H5C2_set_evictions_enabled(H5C2_t * cache_ptr,
+ hbool_t evictions_enabled);
+
+H5_DLL herr_t H5C2_set_prefix(H5C2_t * cache_ptr, char * prefix);
+
+H5_DLL herr_t H5C2_set_skip_flags(H5C2_t * cache_ptr,
+ hbool_t skip_file_checks,
+ hbool_t skip_dxpl_id_checks);
+
+H5_DLL herr_t H5C2_set_trace_file_ptr(H5C2_t * cache_ptr,
+ FILE * trace_file_ptr);
+
+H5_DLL herr_t H5C2_stats(H5C2_t * cache_ptr,
+ const char * cache_name,
+ hbool_t display_detailed_stats);
+
+H5_DLL void H5C2_stats__reset(H5C2_t * cache_ptr);
+
+H5_DLL herr_t H5C2_unpin_entry(H5C2_t * cache_ptr, void * thing);
+
+H5_DLL herr_t H5C2_unprotect(H5C2_t * cache_ptr,
+ hid_t dxpl_id,
+ const H5C2_class_t * type,
+ haddr_t addr,
+ void * thing,
+ unsigned int flags,
+ size_t new_size);
+
+H5_DLL herr_t H5C2_validate_resize_config(H5C2_auto_size_ctl_t * config_ptr,
+ unsigned int tests);
+
+#endif /* !_H5C2private_H */
+
diff --git a/src/H5C2public.h b/src/H5C2public.h
new file mode 100644
index 0000000..e1adff3
--- /dev/null
+++ b/src/H5C2public.h
@@ -0,0 +1,55 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+ * Copyright by the Board of Trustees of the University of Illinois. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5C2public.h
+ * June 4, 2005
+ * John Mainzer
+ *
+ * Purpose: Public include file for cache functions.
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _H5C2public_H
+#define _H5C2public_H
+
+/* Public headers needed by this file */
+#include "H5public.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum H5C2_cache_incr_mode
+{
+ H5C2_incr__off,
+ H5C2_incr__threshold
+};
+
+enum H5C2_cache_decr_mode
+{
+ H5C2_decr__off,
+ H5C2_decr__threshold,
+ H5C2_decr__age_out,
+ H5C2_decr__age_out_with_threshold
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif