diff options
author | John Mainzer <mainzer@hdfgroup.org> | 2004-07-02 19:35:04 (GMT) |
---|---|---|
committer | John Mainzer <mainzer@hdfgroup.org> | 2004-07-02 19:35:04 (GMT) |
commit | c49dd7fa363bdb49416b5587654101064fd547f8 (patch) | |
tree | e1dbeee84ca50b94dfab9b1ed630d890405c6f78 /src | |
parent | 072919b9d7fa341c1a886fb92841efa444f0ffae (diff) | |
download | hdf5-c49dd7fa363bdb49416b5587654101064fd547f8.zip hdf5-c49dd7fa363bdb49416b5587654101064fd547f8.tar.gz hdf5-c49dd7fa363bdb49416b5587654101064fd547f8.tar.bz2 |
[svn-r8791] Purpose: Rewrote metadata cache (H5AC.c, etc.) to improve performance.
Description:
Replaced the old metadata cache with a cache with a modified LRU
replacement policy. This should improve the hit rate.
Solution:
Since we want to flush cache entries in increasing address order, I
used the threaded binary B-tree code to store the cache entries.
There is a fair bit of overhead here, so we may want to consider
other options.
While the code is designed to allow the support of other replacement
algorithms, at present, only a modified version of LRU is supported.
The modified LRU algorithm requires that a user selectable portion
of the cache entries be clean. The clean entries are evicted first
when writes are not permitted. If the pool of clean entries is used
up, the cache grows beyond its user specified maximum size. The
cache can also exceed its maximum size if the combined size of the
protected (or locked) entries exceeds the maximum size of the cache.
Platforms tested:
eirene (serial, parallel, fp), h5committested
Misc. update:
Diffstat (limited to 'src')
-rw-r--r-- | src/H5.c | 2 | ||||
-rw-r--r-- | src/H5AC.c | 1842 | ||||
-rw-r--r-- | src/H5ACprivate.h | 97 | ||||
-rw-r--r-- | src/H5B.c | 52 | ||||
-rw-r--r-- | src/H5C.c | 3490 | ||||
-rw-r--r-- | src/H5Cprivate.h | 374 | ||||
-rw-r--r-- | src/H5Cpublic.h | 40 | ||||
-rw-r--r-- | src/H5E.c | 29 | ||||
-rw-r--r-- | src/H5Edefin.h | 9 | ||||
-rw-r--r-- | src/H5Einit.h | 37 | ||||
-rw-r--r-- | src/H5Eprivate.h | 1 | ||||
-rw-r--r-- | src/H5Epubgen.h | 18 | ||||
-rw-r--r-- | src/H5Eterm.h | 13 | ||||
-rw-r--r-- | src/H5F.c | 8 | ||||
-rw-r--r-- | src/H5Gnode.c | 39 | ||||
-rw-r--r-- | src/H5HG.c | 187 | ||||
-rw-r--r-- | src/H5HL.c | 38 | ||||
-rw-r--r-- | src/H5O.c | 56 | ||||
-rw-r--r-- | src/H5Pdcpl.c | 3 | ||||
-rw-r--r-- | src/H5err.txt | 6 | ||||
-rw-r--r-- | src/Makefile.in | 32 |
21 files changed, 5096 insertions, 1277 deletions
@@ -137,6 +137,8 @@ H5_init_library(void) * & dataset interfaces though, in order to provide them with the proper * property classes. */ + if (H5E_init()<0) + HGOTO_ERROR(H5E_FUNC, H5E_CANTINIT, FAIL, "unable to initialize error interface") if (H5P_init()<0) HGOTO_ERROR(H5E_FUNC, H5E_CANTINIT, FAIL, "unable to initialize property list interface") if (H5F_init()<0) @@ -31,6 +31,14 @@ * Quincey Koziol, 22 Apr 2000 * Turned on "H5AC_SORT_BY_ADDR" * + * John Mainzer, 5/19/04 + * Complete redesign and rewrite. See the header comments for + * H5AC_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 + * faciltate re-use. Re-worked this file again to use H5C. + * *------------------------------------------------------------------------- */ @@ -49,9 +57,7 @@ #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_FPHDF5 @@ -60,56 +66,12 @@ #endif /* H5_HAVE_FPHDF5 */ /* - * Private macros - */ - -/* Hash an address in the file to an offset in the cache */ -#define H5AC_HASH_DIVISOR 8 /* Attempt to spread out the hashing */ - /* This should be the same size as the alignment of */ - /* of the smallest file format object written to the file. */ -#define H5AC_HASH(F,ADDR) H5F_addr_hash((ADDR/H5AC_HASH_DIVISOR),(F)->shared->cache->nslots) - -/* - * Private typedefs & structs - */ - -#ifdef H5AC_DEBUG -typedef struct H5AC_prot_t { - int nprots; /*number of things protected */ - int aprots; /*nelmts of `prot' array */ - H5AC_info_t **slot; /*array of pointers to protected things */ -} H5AC_prot_t; -#endif /* H5AC_DEBUG */ - -struct H5AC_t { - unsigned nslots; /*number of cache slots */ - H5AC_info_t **slot; /*the cache slots, an array of pointers to the cached objects */ - H5AC_info_t **dslot; /*"held object" cache slots, an array of pointers to dirty cached objects */ -#ifdef H5AC_DEBUG - H5AC_prot_t *prot; /*the protected slots */ -#endif /* H5AC_DEBUG */ - int nprots; /*number of protected objects */ -#ifdef H5AC_DEBUG - struct { - unsigned nhits; /*number of cache hits */ - unsigned nmisses; /*number of cache misses */ - unsigned ninits; /*number of cache inits */ - unsigned nflushes; /*number of flushes to disk */ -#ifdef H5_HAVE_PARALLEL - unsigned ndestroys; /*number of cache destroys */ - unsigned nholds; /*number of cache holds */ - unsigned nrestores; /*number of cache restores */ -#endif /* H5_HAVE_PARALLEL */ - } diagnostics[H5AC_NTYPES]; /*diagnostics for each type of object*/ -#endif /* H5AC_DEBUG */ -}; - -/* * 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) */ +/* (Collective set, "block before metadata write" set and */ +/* "library internal" set) */ /* (Global variable definition, declaration is in H5ACprivate.h also) */ hid_t H5AC_dxpl_id=(-1); @@ -123,33 +85,26 @@ static hid_t H5AC_noblock_dxpl_id=(-1); /* (Global variable definition, declaration is in H5ACprivate.h also) */ hid_t H5AC_ind_dxpl_id=(-1); -static H5AC_t *current_cache_g = NULL; /*for sorting */ - -/* Declare a free list to manage the H5AC_t struct */ -H5FL_DEFINE_STATIC(H5AC_t); -/* Declare a free list to manage the cache mapping sequence information */ -H5FL_SEQ_DEFINE_STATIC(unsigned); - -/* Declare a free list to manage the cache slot sequence information */ -H5FL_SEQ_DEFINE_STATIC(H5AC_info_ptr_t); +/* + * Private file-scope function declarations: + */ -#ifdef H5AC_DEBUG -/* Declare a free list to manage the protected slot sequence information */ -H5FL_SEQ_DEFINE_STATIC(H5AC_prot_t); -#endif /* H5AC_DEBUG */ +static herr_t H5AC_check_if_write_permitted(H5F_t *f, + hid_t dxpl_id, + hbool_t * write_permitted_ptr); /*------------------------------------------------------------------------- - * Function: H5AC_init + * Function: H5AC_init * - * Purpose: Initialize the interface from some other layer. + * Purpose: Initialize the interface from some other layer. * - * Return: Success: non-negative + * Return: Success: non-negative * - * Failure: negative + * Failure: negative * - * Programmer: Quincey Koziol + * Programmer: Quincey Koziol * Saturday, January 18, 2003 * * Modifications: @@ -197,7 +152,7 @@ H5AC_init_interface(void) FUNC_ENTER_NOAPI_NOINIT(H5AC_init_interface) /* Sanity check */ - assert(H5P_CLS_DATASET_XFER_g!=(-1)); + 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))) @@ -366,55 +321,83 @@ H5AC_term_interface(void) * * 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 H5C.c. The function is now a + * wrapper for H5C_create(). + * JRM - 6/4/04 *------------------------------------------------------------------------- */ + +const char * H5AC_entry_type_names[H5AC_NTYPES] = +{ + "B-tree nodes", + "symbol table nodes", + "local heaps", + "global heaps", + "object headers" +}; + int -H5AC_create(const H5F_t *f, int size_hint) +H5AC_create(const H5F_t *f, + int UNUSED size_hint) { - H5AC_t *cache = NULL; - int ret_value; /* Return value */ + H5AC_t * cache_ptr = NULL; + int ret_value=1; /* Return value */ FUNC_ENTER_NOAPI(H5AC_create, FAIL) - assert(f); - assert(NULL == f->shared->cache); - - /* If size hint is negative, use the default size */ - if (size_hint < 1) - size_hint = H5AC_NSLOTS; - - if (NULL==(f->shared->cache = cache = H5FL_CALLOC(H5AC_t))) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") - cache->nslots = (unsigned)size_hint; - if (NULL==( cache->slot = H5FL_SEQ_CALLOC(H5AC_info_ptr_t,cache->nslots))) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") - if (NULL==( cache->dslot = H5FL_SEQ_CALLOC(H5AC_info_ptr_t,cache->nslots))) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") -#ifdef H5AC_DEBUG - if ((cache->prot = H5FL_SEQ_CALLOC(H5AC_prot_t,cache->nslots))==NULL) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") -#endif /* H5AC_DEBUG */ + HDassert(f); + HDassert(NULL == f->shared->cache); - /* Set return value */ - ret_value=size_hint; + /* this is test code that should be removed when we start passing + * in proper size hints. + * -- JRM + */ + cache_ptr = H5C_create(H5C__DEFAULT_MAX_CACHE_SIZE, + H5C__DEFAULT_MIN_CLEAN_SIZE, + (H5AC_NTYPES - 1), + &H5AC_entry_type_names, + H5AC_check_if_write_permitted); + + if ( NULL == cache_ptr ) { + + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") + + } else { + + f->shared->cache = (struct H5AC_t *)cache_ptr; + + } done: - if(ret_value<0) { - if(cache!=NULL) { - if(cache->dslot !=NULL) - cache->dslot = H5FL_SEQ_FREE (H5AC_info_ptr_t,cache->dslot); - if(cache->slot !=NULL) - cache->slot = H5FL_SEQ_FREE (H5AC_info_ptr_t,cache->slot); -#ifdef H5AC_DEBUG - if(cache->prot !=NULL) - cache->prot = H5FL_SEQ_FREE (H5AC_prot_t,cache->prot); -#endif /* H5AC_DEBUG */ - f->shared->cache = H5FL_FREE (H5AC_t,f->shared->cache); + + if ( ret_value < 0 ) { + + if ( cache_ptr != NULL ) { + + H5C_dest_empty(cache_ptr); + f->shared->cache = NULL; + } /* end if */ + } /* end if */ FUNC_LEAVE_NOAPI(ret_value) -} + +} /* H5AC_create() */ /*------------------------------------------------------------------------- @@ -432,100 +415,77 @@ done: * * 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 + * *------------------------------------------------------------------------- */ herr_t -H5AC_dest(H5F_t *f, hid_t dxpl_id) +H5AC_dest(H5F_t *f, + hid_t dxpl_id) { - H5AC_t *cache = NULL; + H5AC_t *cache_ptr = NULL; herr_t ret_value=SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(H5AC_dest, FAIL) assert(f); assert(f->shared->cache); - cache = f->shared->cache; + cache_ptr = (H5AC_t *)(f->shared->cache); - if (H5AC_flush(f, dxpl_id, NULL, HADDR_UNDEF, H5F_FLUSH_INVALIDATE) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache") + f->shared->cache = NULL; -#ifdef H5AC_DEBUG - { - unsigned i; - for (i=0; i<cache->nslots; i++) { - cache->prot[i].slot = H5MM_xfree(cache->prot[i].slot); - cache->prot[i].aprots = 0; - cache->prot[i].nprots = 0; - } - cache->prot = H5FL_SEQ_FREE(H5AC_prot_t,cache->prot); - } -#endif + if ( H5C_dest(f, dxpl_id, H5AC_noblock_dxpl_id, cache_ptr) < 0 ) { - cache->dslot = H5FL_SEQ_FREE(H5AC_info_ptr_t,cache->dslot); - cache->slot = H5FL_SEQ_FREE(H5AC_info_ptr_t,cache->slot); - cache->nslots = 0; - f->shared->cache = cache = H5FL_FREE(H5AC_t,cache); + HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "can't destroy cache") + } done: - FUNC_LEAVE_NOAPI(ret_value) -} - - -/*------------------------------------------------------------------------- - * Function: H5AC_compare - * - * Purpose: Compare two hash entries by address. Unused entries are - * all equal to one another and greater than all used entries. - * - * Return: Success: -1, 0, 1 - * - * Failure: never fails - * - * Programmer: Robb Matzke - * matzke@llnl.gov - * Aug 12 1997 - * - * Modifications: - * - *------------------------------------------------------------------------- - */ -static int -H5AC_compare(const void *_a, const void *_b) -{ - unsigned a = *((const unsigned *) _a); - unsigned b = *((const unsigned *) _b); - H5AC_info_t *slot_a; - H5AC_info_t *slot_b; - int ret_value=0; - - /* Use FUNC_ENTER_NOAPI_NOINIT here to avoid performance issues */ - FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5AC_compare) - - assert(current_cache_g); - - /* Create aliases for slots */ - slot_a=current_cache_g->slot[a]; - slot_b=current_cache_g->slot[b]; - - assert(slot_a); - assert(slot_b); - assert(slot_a->type); - assert(slot_b->type); - - if (slot_a->addr < slot_b->addr) { - ret_value=(-1); - } else if (slot_a->addr > slot_b->addr) { - ret_value=1; - } FUNC_LEAVE_NOAPI(ret_value) -} + +} /* H5AC_dest() */ /*------------------------------------------------------------------------- * Function: H5AC_flush * - * Purpose: Flushes (and destroys if DESTROY is non-zero) the specified + * 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 H5AC_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 @@ -546,258 +506,52 @@ H5AC_compare(const void *_a, const void *_b) * 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_dest() in H5C.c, + * and then re-wrote the function as a wrapper for H5C_dest(). + * + * JRM - 6/7/04 + * *------------------------------------------------------------------------- */ herr_t -H5AC_flush(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, haddr_t addr, unsigned flags) +H5AC_flush(H5F_t *f, + hid_t dxpl_id, + const H5AC_class_t *type, + haddr_t addr, + unsigned flags) { - herr_t status; - H5AC_flush_func_t flush=NULL; /* 'flush' callback for an object */ - H5AC_info_t **info; - unsigned *map = NULL; /* Mapping of cache entries */ - hbool_t destroy=(flags&H5F_FLUSH_INVALIDATE)>0; /* Flag for destroying objects */ - hbool_t clear_only=(flags&H5F_FLUSH_CLEAR_ONLY)>0; /* Flag for only clearing objects */ - unsigned nslots; - H5AC_t *cache; - unsigned u; /* Local index variable */ - herr_t ret_value=SUCCEED; /* Return value */ + herr_t status; + herr_t ret_value = SUCCEED; + H5AC_t * cache_ptr; FUNC_ENTER_NOAPI(H5AC_flush, FAIL) - assert(f); - assert(f->shared->cache); + HDassert(f); + HDassert(f->shared->cache); + HDassert(type == NULL); + HDassert(!H5F_addr_defined(addr)); - /* Get local copy of this information */ - cache = f->shared->cache; + cache_ptr = (H5AC_t *)(f->shared->cache); - if (!H5F_addr_defined(addr)) { - unsigned first_flush=1; /* Indicate if this is the first flush */ + status = H5C_flush_cache(f, + dxpl_id, + H5AC_noblock_dxpl_id, + cache_ptr, + flags); - /* - * Sort the cache entries by address since flushing them in - * ascending order by address is much more efficient. - */ - if (NULL==(map=H5FL_SEQ_MALLOC(unsigned,cache->nslots))) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") -#ifdef H5_HAVE_PARALLEL - /* If MPI based VFD is used, do special parallel I/O actions */ - if(IS_H5FD_MPI(f)) { - H5AC_info_t **dinfo; -#ifdef H5AC_DEBUG - H5AC_subid_t type_id; -#endif /* H5AC_DEBUG */ -#ifndef NDEBUG - H5P_genplist_t *dxpl; /* Dataset transfer property list */ - H5FD_mpio_xfer_t xfer_mode; /* I/O transfer 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 */ - assert(xfer_mode == H5FD_MPIO_COLLECTIVE || IS_H5FD_FPHDF5(f)); -#endif /* NDEBUG */ - - /* Create the mapping */ - for (u = nslots = 0; u < cache->nslots; u++) { - info = cache->slot + u; - dinfo = cache->dslot + u; - - /* Move dirty metadata from 'held' slots into 'regular' slots */ - if((*dinfo)!=NULL) { - H5AC_dest_func_t dest; - - /* Various sanity checks */ - assert((*dinfo)->dirty); - assert((*info)!=NULL); - assert((*info)->dirty==0); - -#ifdef H5AC_DEBUG - type_id=(*info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ - - /* Destroy 'current' information */ - dest = (*info)->type->dest; - if ((dest)(f, (*info))<0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "unable to free cached object") - - /* Restore 'held' information back to 'current' information */ - (*info)=(*dinfo); - - /* Clear 'held' information */ - (*dinfo)=NULL; - -#ifdef H5AC_DEBUG - cache->diagnostics[type_id].nrestores++; -#endif /* H5AC_DEBUG */ - } /* end if */ - if ((*info)) - map[nslots++] = u; - } /* end for */ - } /* end if */ - else { -#endif /* H5_HAVE_PARALLEL */ - for (u = nslots = 0; u < cache->nslots; u++) { /*lint !e539 Positive indention is OK */ - if (cache->slot[u]!=NULL) - map[nslots++] = u; - } -#ifdef H5_HAVE_PARALLEL - } /* end else */ -#endif /* H5_HAVE_PARALLEL */ - assert(NULL == current_cache_g); - current_cache_g = cache; - HDqsort(map, nslots, sizeof(unsigned), H5AC_compare); - current_cache_g = NULL; -#ifndef NDEBUG - for (u = 1; u < nslots; u++) - assert(H5F_addr_lt(cache->slot[map[u - 1]]->addr, cache->slot[map[u]]->addr)); -#endif - - /* - * Look at all cache entries. - */ - for (u = 0; u < nslots; u++) { - info = cache->slot + map[u]; - assert(*info); - if (!type || type == (*info)->type) { -#ifdef H5AC_DEBUG - H5AC_subid_t type_id=(*info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ - - /* Clear the dirty flag only, if requested */ - if(clear_only) { - /* Call the callback routine to clear all dirty flags for object */ - if(((*info)->type->clear)(f, *info, destroy)<0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to clear cache"); - } /* end if */ - else { - flush = (*info)->type->flush; - - /* Only block for all the processes on the first piece of metadata */ - if(first_flush && (*info)->dirty) { - status = (flush)(f, dxpl_id, destroy, (*info)->addr, (*info)); - first_flush=0; - } /* end if */ - else - status = (flush)(f, H5AC_noblock_dxpl_id, destroy, (*info)->addr, (*info)); - if (status < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache") -#ifdef H5AC_DEBUG - cache->diagnostics[type_id].nflushes++; -#endif /* H5AC_DEBUG */ - } /* end else */ - - /* Destroy entry also, if asked */ - if (destroy) - (*info)= NULL; - } - } + if ( status < 0 ) { - /* - * If there are protected object then fail. However, everything - * else should have been flushed. - */ - if (cache->nprots > 0) - HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, "cache has protected items") - } else { - u = H5AC_HASH(f, addr); - info = cache->slot + u; -#ifdef H5_HAVE_PARALLEL - /* If MPI based VFD is used, do special parallel I/O actions */ - if(IS_H5FD_MPI(f)) { - H5AC_info_t **dinfo; -#ifdef H5AC_DEBUG - H5AC_subid_t type_id; -#endif /* H5AC_DEBUG */ -#ifndef NDEBUG - H5P_genplist_t *dxpl; /* Dataset transfer property list */ - H5FD_mpio_xfer_t xfer_mode; /* I/O transfer 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 */ - assert(xfer_mode==H5FD_MPIO_COLLECTIVE); -#endif /* NDEBUG */ - - dinfo = cache->dslot + u; - - /* Restore dirty metadata from 'held' slot to 'current' slot */ - if((*dinfo)!=NULL) { - H5AC_dest_func_t dest; - - /* Various sanity checks */ - assert((*dinfo)->dirty); - assert((*info)!=NULL); - assert((*info)->dirty==0); - -#ifdef H5AC_DEBUG - type_id=(*info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ - - /* Destroy 'current' information */ - dest = (*info)->type->dest; - if ((dest)(f, (*info))<0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "unable to free cached object") - - /* Restore 'held' information back to 'current' information */ - (*info)=(*dinfo); - - /* Clear 'held' information */ - (*dinfo)=NULL; - -#ifdef H5AC_DEBUG - cache->diagnostics[type_id].nrestores++; -#endif /* H5AC_DEBUG */ - } /* end if */ - } /* end if */ -#endif /* H5_HAVE_PARALLEL */ - if ((*info) && (!type || (*info)->type == type) && - H5F_addr_eq((*info)->addr, addr)) { -#ifdef H5AC_DEBUG - H5AC_subid_t type_id=(*info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ - - /* - * Flush just this entry. - */ - - /* Clear the dirty flag only, if requested */ - if(clear_only) { - /* Call the callback routine to clear all dirty flags for object */ - if(((*info)->type->clear)(f, *info, destroy)<0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to clear cache"); - } /* end if */ - else { - flush = (*info)->type->flush; - if((flush)(f, dxpl_id, destroy, (*info)->addr, (*info)) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush object") -#ifdef H5AC_DEBUG - cache->diagnostics[type_id].nflushes++; -#endif /* H5AC_DEBUG */ - } /* end else */ - - /* Destroy entry also, if asked */ - if (destroy) - (*info)= NULL; - } /* end if */ - } /* end else */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't flush entry.") + } done: - if(map!=NULL) - map = H5FL_SEQ_FREE(unsigned,map); FUNC_LEAVE_NOAPI(ret_value) -} + +} /* H5AC_flush() */ /*------------------------------------------------------------------------- @@ -824,164 +578,138 @@ done: * 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(). + * *------------------------------------------------------------------------- */ + herr_t -H5AC_set(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, haddr_t addr, void *thing) +H5AC_set(H5F_t *f, + hid_t dxpl_id, + const H5AC_class_t *type, + haddr_t addr, + void *thing) { - unsigned idx; - H5AC_info_t **info; - H5AC_t *cache; - herr_t ret_value = SUCCEED; /* Return value */ + herr_t result; + herr_t ret_value = SUCCEED; /* Return value */ + H5AC_info_t * info_ptr; + H5AC_t * cache_ptr; FUNC_ENTER_NOAPI(H5AC_set, FAIL) - assert(f); - assert(f->shared->cache); - assert(type); - assert(type->flush); - assert(H5F_addr_defined(addr)); - assert(thing); + HDassert(f); + HDassert(f->shared->cache); + HDassert(type); + HDassert(type->flush); + HDassert(type->size); + HDassert(H5F_addr_defined(addr)); + HDassert(thing); - /* Get local copy of this information */ - idx = H5AC_HASH(f, addr); - cache = f->shared->cache; - info = cache->slot + idx; - -#ifdef H5AC_DEBUG - { - H5AC_prot_t *prot = NULL; - int i; + cache_ptr = (H5AC_t *)(f->shared->cache); + info_ptr = (H5AC_info_t *)thing; - prot = cache->prot + idx; - for (i = 0; i < prot->nprots; i++) - assert(H5F_addr_ne(addr, prot->slot[i]->addr)); - } -#endif + info_ptr->addr = addr; + info_ptr->type = type; + info_ptr->protected = FALSE; #ifdef H5_HAVE_PARALLEL - /* - * If MPI based VFD is used, do special parallel I/O actions +#ifdef H5_HAVE_FPHDF5 + /* In the flexible parallel case, the cache is always empty. Thus + * we simply flush and destroy entry we have just received. */ - if(IS_H5FD_MPI(f)) { - H5AC_info_t **dinfo; - H5P_genplist_t *dxpl; /* Dataset transfer property list */ - H5FD_mpio_xfer_t xfer_mode; /* I/O transfer mode property value */ -#ifdef H5AC_DEBUG - H5AC_subid_t type_id; -#endif /* H5AC_DEBUG */ + { + H5FD_t * lf; + unsigned req_id; + H5FP_status_t status; - /* 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") + HDassert(f->shared->lf); - /* 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") - - /* Get pointer to 'held' information */ - dinfo = cache->dslot + idx; - - /* Sanity check transfer mode */ - if(xfer_mode==H5FD_MPIO_COLLECTIVE) { - /* Check for dirty metadata */ - if(*dinfo) { - H5AC_dest_func_t dest; - - /* Various sanity checks */ - assert((*dinfo)->dirty); - assert((*info)!=NULL); - assert((*info)->dirty==0); - -#ifdef H5AC_DEBUG - type_id=(*info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ - - /* Destroy 'current' information */ - dest = (*info)->type->dest; - if ((dest)(f, (*info))<0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "unable to free cached object") - - /* Restore 'held' information back to 'current' information */ - (*info)=(*dinfo); - - /* Clear 'held' information */ - (*dinfo)=NULL; - -#ifdef H5AC_DEBUG - cache->diagnostics[type_id].nrestores++; -#endif /* H5AC_DEBUG */ - } /* end if */ - } /* end if */ - else { - /* Sanity check */ - assert((*dinfo)==NULL); - assert(xfer_mode==H5FD_MPIO_INDEPENDENT); - - /* Make certain there will be no write of dirty metadata */ - if((*info) && (*info)->dirty) { - /* Sanity check new item */ - assert(((H5AC_info_t*)thing)->dirty==0); - - /* 'Hold' the current metadata for later */ - (*dinfo)=(*info); - - /* Reset the 'current' metadata, so it doesn't get flushed */ - (*info)=NULL; - -#ifdef H5AC_DEBUG - cache->diagnostics[(*dinfo)->type->id].nholds++; -#endif /* H5AC_DEBUG */ - } /* end if */ - } /* end else */ - } /* end if */ -#endif /* H5_HAVE_PARALLEL */ + lf = f->shared->lf; - /* Flush any object already in cache slot */ - if ((*info)) { -#ifdef H5AC_DEBUG - H5AC_subid_t type_id=(*info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ + if ( H5FD_is_fphdf5_driver(lf) ) { - if ((*info)->type->flush(f, dxpl_id, TRUE, (*info)->addr, (*info)) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush object") + /* + * This is the FPHDF5 driver. Grab a lock for this piece of + * metadata from the SAP. Bail-out quickly if we're unable to do + * that. In the case of the FPHDF5 driver, the local cache is + * turned off. We lock the address then write the data to the SAP. + * We do this because the cache is off and thus cannot retain the + * data which has just been added to it. We will get it from the + * SAP as needed in the future. + */ + result = H5FP_request_lock(H5FD_fphdf5_file_id(lf), addr, + H5FP_LOCK_WRITE, TRUE, &req_id, &status); -#ifdef H5AC_DEBUG - cache->diagnostics[type_id].nflushes++; -#endif /* H5AC_DEBUG */ - } /* end if */ + if ( result < 0 ) { +#if 0 + HDfprintf(stdout, "H5AC_set: Lock failed.\n"); + /* + * FIXME: Check the status variable. If the lock is got + * by some other process, we can loop and wait or bail + * out of this function + */ + HDfprintf(stderr, + "Couldn't get lock for metadata at address %a\n", + addr); +#endif /* 0 */ + HGOTO_ERROR(H5E_FPHDF5, H5E_CANTLOCK, FAIL, \ + "can't lock data on SAP!") + } - /* Cache this item */ - (*info) = thing; - (*info)->type = type; - (*info)->addr = addr; + /* write the metadata to the SAP. */ -#ifdef H5_HAVE_FPHDF5 - if (H5FD_is_fphdf5_driver(f->shared->lf)) { -#ifdef H5AC_DEBUG - H5AC_subid_t type_id = (*info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ - - /* - * We want to write this metadata to the SAP right now. This will - * keep all of the participating processes in sync. - */ - if ((*info)->type->flush(f, dxpl_id, FALSE, (*info)->addr, *info) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush object") - -#ifdef H5AC_DEBUG - ++cache->diagnostics[type_id].nflushes; -#endif /* H5AC_DEBUG */ + result = (info_ptr->type->flush)(f, dxpl_id, TRUE, + info_ptr->addr, info_ptr); + + if ( result < 0 ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } + + /* and then release the lock */ + + result = H5FP_request_release_lock(H5FD_fphdf5_file_id(lf), addr, + TRUE, &req_id, &status); + if ( result < 0 ) { + + HGOTO_ERROR(H5E_FPHDF5, H5E_CANTUNLOCK, FAIL, \ + "can't unlock data on SAP!") + } + + HGOTO_DONE(SUCCEED); + } } #endif /* H5_HAVE_FPHDF5 */ +#endif /* H5_HAVE_PARALLEL */ -#ifdef H5AC_DEBUG - ++cache->diagnostics[type->id].ninits; -#endif /* H5AC_DEBUG */ + result = H5C_insert_entry(f, + dxpl_id, + H5AC_noblock_dxpl_id, + cache_ptr, + type, + addr, + thing); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "H5C_insert_entry() failed") + } done: + FUNC_LEAVE_NOAPI(ret_value) -} + +} /* H5AC_set() */ /*------------------------------------------------------------------------- @@ -1003,206 +731,101 @@ done: * 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(). + * *------------------------------------------------------------------------- */ + herr_t -H5AC_rename(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, haddr_t old_addr, +H5AC_rename(H5F_t *f, + hid_t UNUSED dxpl_id, + const H5AC_class_t *type, + haddr_t old_addr, haddr_t new_addr) { - unsigned old_idx, new_idx; - H5AC_flush_func_t flush; - H5AC_t *cache; - H5AC_info_t **new_info = NULL; - H5AC_info_t **old_info = NULL; - herr_t ret_value=SUCCEED; /* Return value */ + herr_t result; + herr_t ret_value = SUCCEED; /* Return value */ + H5AC_t * cache_ptr; FUNC_ENTER_NOAPI(H5AC_rename, FAIL) - assert(f); - assert(f->shared->cache); - assert(type); + HDassert(f); + HDassert(f->shared->cache); + HDassert(type); + HDassert(H5F_addr_defined(old_addr)); + HDassert(H5F_addr_defined(new_addr)); + HDassert(H5F_addr_ne(old_addr, new_addr)); - /* Get local copy of this information */ - old_idx = H5AC_HASH(f, old_addr); - new_idx = H5AC_HASH(f, new_addr); - cache = f->shared->cache; - new_info = cache->slot + new_idx; - old_info = cache->slot + old_idx; - -#ifdef H5AC_DEBUG - { - H5AC_prot_t *prot = NULL; - int i; - - prot = cache->prot + old_idx; - for (i = 0; i < prot->nprots; i++) - assert(H5F_addr_ne(old_addr, prot->slot[i]->addr)); - prot = cache->prot + new_idx; - for (i = 0; i < prot->nprots; i++) - assert(H5F_addr_ne(new_addr, prot->slot[i]->addr)); - } -#endif + cache_ptr = (H5AC_t *)(f->shared->cache); - /* - * We don't need to do anything if the object isn't cached or if the - * new hash value is the same as the old one. +#ifdef H5_HAVE_PARALLEL +#ifdef H5_HAVE_FPHDF5 + /* In the flexible parallel case, the cache is always empty. + * Thus H5AC_rename() has nothing to do by definition. */ - assert(old_info); - if (H5F_addr_ne((*old_info)->addr, old_addr) || (*old_info)->type!=type) - HGOTO_DONE(SUCCEED) - if (old_idx == new_idx) { - (*old_info)->addr = new_addr; - HGOTO_DONE(SUCCEED) - } + { + H5FD_t * lf; -#ifdef H5_HAVE_PARALLEL - /* If MPI based VFD is used, do special parallel I/O actions */ - if(IS_H5FD_MPI(f)) { - H5AC_info_t **new_dinfo; -#ifdef H5AC_DEBUG - H5AC_subid_t type_id; -#endif /* H5AC_DEBUG */ - H5P_genplist_t *dxpl; /* Dataset transfer property list */ - H5FD_mpio_xfer_t xfer_mode; /* I/O transfer mode property value */ + HDassert(f->shared->lf); - /* 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") + lf = f->shared->lf; - /* 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") - - /* Get pointer to new 'held' information */ - new_dinfo = cache->dslot + new_idx; - - /* Sanity check transfer mode */ - if(xfer_mode==H5FD_MPIO_COLLECTIVE) { - /* Check for dirty metadata */ - if(*new_dinfo) { - H5AC_dest_func_t dest; - - /* Various sanity checks */ - assert((*new_dinfo)->dirty); - assert((*new_info)!=NULL); - assert((*new_info)->dirty==0); - -#ifdef H5AC_DEBUG - type_id=(*new_info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ - - /* Destroy 'current' information */ - dest = (*new_info)->type->dest; - if ((dest)(f, (*new_info))<0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "unable to free cached object") - - /* Restore 'held' information back to 'current' information */ - (*new_info)=(*new_dinfo); - - /* Clear 'held' information */ - (*new_dinfo)=NULL; - -#ifdef H5AC_DEBUG - cache->diagnostics[type_id].nrestores++; -#endif /* H5AC_DEBUG */ - } /* end if */ - } /* end if */ - else { - /* Sanity check that there will be no write of dirty metadata */ - assert((*new_dinfo)==NULL); - assert(xfer_mode==H5FD_MPIO_INDEPENDENT); - - /* Make certain there will be no write of dirty metadata */ - if((*new_info) && (*new_info)->dirty) { - /* Sanity check that we won't put two pieces of dirty metadata in same cache location */ - assert((*old_info)->dirty==0); - - /* 'Hold' the current metadata for later */ - (*new_dinfo)=(*new_info); - - /* Reset the 'current' metadata, so it doesn't get flushed */ - (*new_info)=NULL; - -#ifdef H5AC_DEBUG - cache->diagnostics[(*new_dinfo)->type->id].nholds++; -#endif /* H5AC_DEBUG */ - } /* end if */ - } /* end else */ - } /* end if */ -#endif /* H5_HAVE_PARALLEL */ + if ( H5FD_is_fphdf5_driver(lf) ) { - /* - * Free the item from the destination cache line. - */ - if (*new_info) { -#ifdef H5AC_DEBUG - H5AC_subid_t type_id=(*new_info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ - - flush = (*new_info)->type->flush; - if ( (flush)(f, dxpl_id, TRUE, (*new_info)->addr, (*new_info)) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush object") -#ifdef H5AC_DEBUG - cache->diagnostics[type_id].nflushes++; -#endif /* H5AC_DEBUG */ + HGOTO_DONE(SUCCEED); + } } +#endif /* H5_HAVE_FPHDF5 */ +#endif /* H5_HAVE_PARALLEL */ - /* - * Move the source to the destination (it might not be cached) - */ - (*new_info)= (*old_info); - (*new_info)->addr = new_addr; + result = H5C_rename_entry(f, + cache_ptr, + type, + old_addr, + new_addr); -#ifdef H5_HAVE_PARALLEL - /* If MPI based VFD is used, do special parallel I/O actions */ - if(IS_H5FD_MPI(f)) { - H5AC_info_t **old_dinfo; -#ifdef H5AC_DEBUG - H5AC_subid_t type_id; -#endif /* H5AC_DEBUG */ - - /* Get pointer to new 'held' information */ - old_dinfo = cache->dslot + old_idx; - - /* Check for 'held' metadata in old location & restore it, if so */ - if(*old_dinfo) { - /* Sanity check */ - assert((*old_dinfo)->dirty); - -#ifdef H5AC_DEBUG - type_id=(*old_info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ - - /* Restore 'held' information back to 'current' information */ - (*old_info)=(*old_dinfo); - - /* Clear 'held' information */ - (*old_dinfo)=NULL; - -#ifdef H5AC_DEBUG - cache->diagnostics[type_id].nrestores++; -#endif /* H5AC_DEBUG */ - } /* end if */ - else - (*old_info)= NULL; - } /* end if */ - else { -#endif /* H5_HAVE_PARALLEL */ + if ( result < 0 ) { - (*old_info)= NULL; -#ifdef H5_HAVE_PARALLEL - } /* end else */ -#endif /* H5_HAVE_PARALLEL */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ + "H5C_rename_entry() failed.") + } done: + FUNC_LEAVE_NOAPI(ret_value) -} + +} /* H5AC_rename() */ /*------------------------------------------------------------------------- * Function: H5AC_protect * - * Purpose: Similar to H5AC_find() except the object is removed from + * 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 H5AC_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 + * H5AC_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 H5AC_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. @@ -1228,261 +851,190 @@ done: * 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 H5C_protect() + * in H5C.c, and then re-wrote the function as a wrapper for + * H5C_protect(). + * *------------------------------------------------------------------------- */ + void * -H5AC_protect(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, haddr_t addr, - const void *udata1, void *udata2, H5AC_protect_t +H5AC_protect(H5F_t *f, + hid_t dxpl_id, + const H5AC_class_t *type, + haddr_t addr, + const void *udata1, + void *udata2, + H5AC_protect_t #ifndef H5_HAVE_FPHDF5 UNUSED #endif /* H5_HAVE_FPHDF5 */ rw) { - unsigned idx; /* Index in cache */ - void *thing = NULL; - H5AC_t *cache = NULL; - H5AC_info_t **info; - void *ret_value; /* Return value */ -#ifdef H5_HAVE_FPHDF5 - H5FD_t *lf; - unsigned req_id; - H5FP_status_t status; -#endif /* H5_HAVE_FPHDF5 */ - -#ifdef H5AC_DEBUG - H5AC_prot_t *prot = NULL; - static int ncalls = 0; - - if (0 == ncalls++) { - if (H5DEBUG(AC)) { - fprintf(H5DEBUG(AC), "H5AC: debugging cache (expensive)\n"); - } - } -#endif + void * thing = NULL; + H5AC_t * cache_ptr = NULL; + void * ret_value; /* Return value */ FUNC_ENTER_NOAPI(H5AC_protect, NULL) /* check args */ - assert(f); - assert(f->shared->cache); - assert(type); - assert(type->load); - assert(type->flush); - assert(H5F_addr_defined(addr)); - - /* Get local copy of this information */ - idx = H5AC_HASH(f, addr); - cache = f->shared->cache; - info = cache->slot + idx; -#ifdef H5AC_DEBUG - prot = cache->prot + idx; -#endif /* H5AC_DEBUG */ + HDassert(f); + HDassert(f->shared->cache); + HDassert(type); + HDassert(type->flush); + HDassert(type->load); + HDassert(H5F_addr_defined(addr)); + + cache_ptr = (H5AC_t *)(f->shared->cache); #ifdef H5_HAVE_PARALLEL #ifdef H5_HAVE_FPHDF5 - lf = f->shared->lf; - - if (H5FD_is_fphdf5_driver(lf)) { - /* - * This is the FPHDF5 driver. Grab a lock for this piece of - * metadata from the SAP. Bail-out quickly if we're unable to do - * that. In the case of the FPHDF5 driver, the local cache is - * effectively turned off. We lock the address then load the data - * from the SAP (or file) directly. We do this because at any one - * time the data on the SAP will be different than what's on the - * local process. - */ - if (H5FP_request_lock(H5FD_fphdf5_file_id(lf), addr, - rw == H5AC_WRITE ? H5FP_LOCK_WRITE : H5FP_LOCK_READ, - TRUE, &req_id, &status) < 0) { -#if 0 - HDfprintf(stdout, "H5AC_protect: Lock failed.\n"); + /* The following code to support flexible parallel is a direct copy + * from the old version of the cache with slight edits. It should + * be viewed with as much suspicion as the rest of the FP code. + * JRM - 5/26/04 + */ + { + H5FD_t * lf; + unsigned req_id; + H5FP_status_t status; + H5AC_info_t * info_ptr; + + HDassert(f->shared->lf); + + lf = f->shared->lf; + + if ( H5FD_is_fphdf5_driver(lf) ) { + /* - * FIXME: Check the status variable. If the lock is got - * by some other process, we can loop and wait or bail - * out of this function + * This is the FPHDF5 driver. Grab a lock for this piece of + * metadata from the SAP. Bail-out quickly if we're unable to do + * that. In the case of the FPHDF5 driver, the local cache is + * effectively turned off. We lock the address then load the data + * from the SAP (or file) directly. We do this because at any one + * time the data on the SAP will be different than what's on the + * local process. */ - HDfprintf(stderr, "Couldn't get lock for metadata at address %a\n", addr); + if ( H5FP_request_lock(H5FD_fphdf5_file_id(lf), addr, + rw == H5AC_WRITE ? H5FP_LOCK_WRITE : H5FP_LOCK_READ, + TRUE, &req_id, &status) < 0) { +#if 0 + HDfprintf(stdout, "H5AC_protect: Lock failed.\n"); + /* + * FIXME: Check the status variable. If the lock is got + * by some other process, we can loop and wait or bail + * out of this function + */ + HDfprintf(stderr, + "Couldn't get lock for metadata at address %a\n", + addr); #endif /* 0 */ - HGOTO_ERROR(H5E_FPHDF5, H5E_CANTLOCK, NULL, "can't lock data on SAP!") - } + HGOTO_ERROR(H5E_FPHDF5, H5E_CANTLOCK, NULL, \ + "can't lock data on SAP!") + } + + /* Load a thing from the SAP. */ + if ( NULL == (thing = type->load(f, dxpl_id, addr, + udata1, udata2)) ) { - /* Load a thing from the SAP. */ - if (NULL == (thing = type->load(f, dxpl_id, addr, udata1, udata2))) { #if 0 - HDfprintf(stdout, - "%s: Load failed. addr = %a, type->id = %d.\n", - "H5AC_protect", - addr, - (int)(type->id)); + HDfprintf(stdout, + "%s: Load failed. addr = %a, type->id = %d.\n", + "H5AC_protect", + addr, + (int)(type->id)); #endif /* 0 */ - HCOMMON_ERROR(H5E_CACHE, H5E_CANTLOAD, "unable to load object") + HCOMMON_ERROR(H5E_CACHE, H5E_CANTLOAD, "unable to load object") - if (H5FP_request_release_lock(H5FD_fphdf5_file_id(lf), addr, - TRUE, &req_id, &status) < 0) - HGOTO_ERROR(H5E_FPHDF5, H5E_CANTUNLOCK, NULL, "can't unlock data on SAP!") + if (H5FP_request_release_lock(H5FD_fphdf5_file_id(lf), addr, + TRUE, &req_id, &status) < 0) + HGOTO_ERROR(H5E_FPHDF5, H5E_CANTUNLOCK, NULL, \ + "can't unlock data on SAP!") - HGOTO_DONE(NULL); - } + HGOTO_DONE(NULL); + } + + info_ptr = (H5AC_info_t *)thing; - HGOTO_DONE(thing); - } -#endif /* H5_HAVE_FPHDF5 */ + HDassert(info_ptr->dirty == FALSE); - /* If MPI based VFD is used, do special parallel I/O actions */ - if (IS_H5FD_MPI(f)) { - H5AC_info_t **dinfo; - - /* Get pointer to new 'held' information */ - dinfo = cache->dslot + idx; - - /* Check for 'held' metadata in location & handle it */ - if(*dinfo) { - /* Sanity checks */ - assert((*dinfo)->dirty); - assert((*info)); - assert((*info)->dirty == FALSE); - assert((*dinfo)->addr != (*info)->addr); - - /* Is 'held' metadata the metadata we are looking for? */ - if (H5F_addr_eq((*dinfo)->addr, addr) -#ifdef H5AC_DEBUG - && (*dinfo)->type==type -#endif /* H5AC_DEBUG */ - ) { -#ifndef H5AC_DEBUG - /* Sanity check that the object in the cache is the correct type */ - assert((*dinfo)->type == type); -#endif /* H5AC_DEBUG */ - - /* The object is already cached; simply remove it from the cache. */ - thing = (*dinfo); - (*dinfo)->type = NULL; - (*dinfo)->addr = HADDR_UNDEF; - (*dinfo) = NULL; -#ifdef H5AC_DEBUG - ++cache->diagnostics[(*dinfo)->type->id].nhits; -#endif /* H5AC_DEBUG */ - } /* end if */ - else { - /* - * 'held' metadata isn't what we are looking for, but - * check for 'current' metadata - */ - if (H5F_addr_eq((*info)->addr, addr) -#ifdef H5AC_DEBUG - && (*info)->type==type -#endif /* H5AC_DEBUG */ - ) { -#ifndef H5AC_DEBUG - /* Sanity check that the object in the cache is the correct type */ - assert((*info)->type == type); -#endif /* H5AC_DEBUG */ - - /* - * The object is already cached; remove it from the cache. - * and bring the 'held' object into the 'regular' information - */ - thing = (*info); - (*info)->type = NULL; - (*info)->addr = HADDR_UNDEF; - (*info) = (*dinfo); - (*dinfo) = NULL; -#ifdef H5AC_DEBUG - ++cache->diagnostics[(*info)->type->id].nhits; -#endif /* H5AC_DEBUG */ - } /* end if */ - } /* end else */ - } /* end if */ - } /* end if */ + info_ptr->addr = addr; + info_ptr->type = type; + info_ptr->protected = TRUE; - /* Check if we've already found the object to protect */ - if (thing == NULL) { -#endif /* H5_HAVE_PARALLEL */ + if ( (type->size)(f, thing, &(info_ptr->size)) < 0 ) { - if ((*info) && H5F_addr_eq(addr,(*info)->addr) -#ifdef H5AC_DEBUG - && (*info)->type == type -#endif /* H5AC_DEBUG */ - ) { -#ifndef H5AC_DEBUG - /* Sanity check that the object in the cache is the correct type */ - assert((*info)->type == type); -#endif /* H5AC_DEBUG */ - - /* The object is already cached; simply remove it from the cache. */ - thing = (*info); - (*info)->type = NULL; - (*info)->addr = HADDR_UNDEF; - (*info) = NULL; -#ifdef H5AC_DEBUG - ++cache->diagnostics[(*info)->type->id].nhits; -#endif /* H5AC_DEBUG */ - } else { -#ifdef H5AC_DEBUG - /* - * Check that the requested thing isn't protected, for protected things - * can only be modified through the pointer already handed out by the - * H5AC_protect() function. - */ - int i; + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, NULL, \ + "Can't get size of thing") + } - for (i = 0; i < prot->nprots; i++) - assert(H5F_addr_ne(addr, prot->slot[i]->addr)); -#endif /* H5AC_DEBUG */ + HDassert(info_ptr->size < H5C_MAX_ENTRY_SIZE); - /* - * Load a new thing. If it can't be loaded, then return an error - * without preempting anything. - */ - if (NULL == (thing = (type->load)(f, dxpl_id, addr, udata1, udata2))) - HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "unable to load object") + info_ptr->next = NULL; + info_ptr->prev = NULL; + info_ptr->aux_next = NULL; + info_ptr->aux_prev = NULL; -#ifdef H5AC_DEBUG - ++cache->diagnostics[type->id].nmisses; -#endif /* H5AC_DEBUG */ + HGOTO_DONE(thing); } -#ifdef H5_HAVE_PARALLEL - } /* end if */ -#endif /* H5_HAVE_PARALLEL */ + } +#endif /* H5_HAVE_FPHDF5 */ +#endif /* H5_HAVE_PARALLEL */ -#ifdef H5AC_DEBUG - /* - * Add the protected object to the protect debugging fields of the - * cache. - */ - if (prot->nprots >= prot->aprots) { - size_t na = prot->aprots + 10; - H5AC_info_t **x; + thing = H5C_protect(f, + dxpl_id, + H5AC_noblock_dxpl_id, + cache_ptr, + type, + addr, + udata1, + udata2); - if (NULL == (x = H5MM_realloc(prot->slot, na * sizeof(H5AC_info_t *)))) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") + if ( thing == NULL ) { - prot->aprots = (int)na; - prot->slot = x; + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C_protect() failed.") } - prot->slot[prot->nprots]= thing; - prot->slot[prot->nprots]->type = type; - prot->slot[prot->nprots]->addr = addr; - ++prot->nprots; -#endif /* H5AC_DEBUG */ - /* Set return value */ ret_value = thing; done: - if (ret_value) - ++cache->nprots; FUNC_LEAVE_NOAPI(ret_value) -} + +} /* H5AC_protect() */ /*------------------------------------------------------------------------- * Function: H5AC_unprotect * - * Purpose: This function should be called to undo the effect of + * Purpose: Undo an H5AC_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 H5AC_protect() and the THING + * argument must be the value returned by that call to + * H5AC_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 * H5AC_protect(). The TYPE and ADDR arguments should be the * same as the corresponding call to H5AC_protect() and the * THING argument should be the value returned by H5AC_protect(). @@ -1509,212 +1061,127 @@ done: * 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(). + * *------------------------------------------------------------------------- */ herr_t -H5AC_unprotect(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, haddr_t addr, - void *thing, hbool_t deleted) +H5AC_unprotect(H5F_t *f, + hid_t dxpl_id, + const H5AC_class_t *type, + haddr_t addr, + void *thing, + hbool_t deleted) { - unsigned idx; - H5AC_flush_func_t flush; - H5AC_t *cache = NULL; - H5AC_info_t **info; + herr_t result; herr_t ret_value = SUCCEED; /* Return value */ -#ifdef H5_HAVE_FPHDF5 - H5FD_t *lf; - unsigned req_id; - H5FP_status_t status; -#endif /* H5_HAVE_FPHDF5 */ - + H5AC_info_t * info_ptr; + H5AC_t * cache_ptr = NULL; FUNC_ENTER_NOAPI(H5AC_unprotect, FAIL) - /* check args */ - assert(f); - assert(f->shared->cache); - assert(type); - assert(type->flush); - assert(H5F_addr_defined(addr)); - assert(thing); - - /* Get local copy of this information */ - idx = H5AC_HASH(f, addr); - cache = f->shared->cache; - info = cache->slot + idx; - -#if defined(H5_HAVE_PARALLEL) && defined(H5_HAVE_FPHDF5) - lf = f->shared->lf; - - if (H5FD_is_fphdf5_driver(lf)) { - /* - * FIXME: If the metadata is *really* deleted at this point - * (deleted == TRUE), we need to send a request to the SAP - * telling it to remove that bit of metadata from its cache. - */ - if (H5FP_request_release_lock(H5FD_fphdf5_file_id(lf), addr, - TRUE, &req_id, &status) < 0) - HGOTO_ERROR(H5E_FPHDF5, H5E_CANTUNLOCK, FAIL, "can't unlock data on SAP!") - - /* Flush a thing to the SAP */ - if (thing) { - if (((H5AC_info_t *)thing)->dirty) { - if (type->flush(f, dxpl_id, FALSE, addr, thing) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush object") - -#ifdef H5AC_DEBUG - ++cache->diagnostics[type_id].nflushes; -#endif /* H5AC_DEBUG */ - } + HDassert(f); + HDassert(f->shared->cache); + HDassert(type); + HDassert(type->clear); + HDassert(type->flush); + HDassert(H5F_addr_defined(addr)); + HDassert(thing); - /* Always clear/delete the object from the local cache */ - if (type->clear(f, thing, TRUE) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "unable to free object") - } + cache_ptr = (H5AC_t *)(f->shared->cache); + info_ptr = (H5AC_info_t *)thing; - /* Exit now. The FPHDF5 stuff is finished. */ - HGOTO_DONE(SUCCEED); - } -#endif /* H5_HAVE_PARALLEL && H5_HAVE_FPHDF5 */ + HDassert( info_ptr->addr == addr ); + HDassert( info_ptr->type == type ); -#ifdef H5AC_DEBUG - /* - * Remove the object's protect data to indicate that it is no longer - * protected. +#ifdef H5_HAVE_PARALLEL +#ifdef H5_HAVE_FPHDF5 + /* The following code to support flexible parallel is a direct copy + * from the old version of the cache with slight edits. It should + * be viewed with as much suspicion as the rest of the FP code. + * JRM - 5/26/04 */ { - int found = FALSE, i; - H5AC_prot_t *prot = cache->prot + idx; - - for (i = 0; i < prot->nprots && !found; ++i) { - if (H5F_addr_eq(addr, prot->slot[i]->addr)) { - assert(prot->slot[i]->type == type); - HDmemmove(prot->slot + i, prot->slot + i + 1, - ((prot->nprots - i) - 1) * sizeof(H5AC_info_t *)); - --prot->nprots; - found = TRUE; - } - } + H5FD_t * lf; + unsigned req_id; + H5FP_status_t status; - assert(found); - } -#endif /* H5AC_DEBUG */ + HDassert(f->shared->lf); - /* Don't restore deleted objects to the cache */ - if (!deleted) { -#ifdef H5_HAVE_PARALLEL - /* If MPI based VFD is used, do special parallel I/O actions */ - if (IS_H5FD_MPI(f)) { - H5AC_info_t **dinfo; - H5P_genplist_t *dxpl; /* Dataset transfer property list */ - H5FD_mpio_xfer_t xfer_mode; /* I/O transfer mode property value */ -#ifdef H5AC_DEBUG - H5AC_subid_t type_id; -#endif /* H5AC_DEBUG */ - - /* 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") - - /* Get pointer to 'held' information */ - dinfo = cache->dslot + idx; - - /* Sanity check transfer mode */ - if (xfer_mode == H5FD_MPIO_COLLECTIVE) { - /* Check for dirty metadata */ - if (*dinfo) { - H5AC_dest_func_t dest; - - /* Various sanity checks */ - assert((*dinfo)->dirty); - assert((*info) != NULL); - assert((*info)->dirty == 0); -#ifdef H5AC_DEBUG - type_id = (*info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ - - /* Destroy 'current' information */ - dest = (*info)->type->dest; - - if ((dest)(f, (*info)) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "unable to free cached object") - - /* Restore 'held' information back to 'current' information */ - (*info) = (*dinfo); - - /* Clear 'held' information */ - (*dinfo) = NULL; -#ifdef H5AC_DEBUG - ++cache->diagnostics[type_id].nrestores; -#endif /* H5AC_DEBUG */ - } /* end if */ - } /* end if */ - else { - /* Sanity check */ - assert((*dinfo) == NULL); - assert(xfer_mode == H5FD_MPIO_INDEPENDENT); - - /* Make certain there will be no write of dirty metadata */ - if ((*info) && (*info)->dirty) { - /* Sanity check new item */ - assert(((H5AC_info_t*)thing)->dirty == 0); - - /* 'Hold' the current metadata for later */ - (*dinfo) = (*info); - - /* Reset the 'current' metadata, so it doesn't get flushed */ - (*info) = NULL; -#ifdef H5AC_DEBUG - ++cache->diagnostics[(*dinfo)->type->id].nholds; -#endif /* H5AC_DEBUG */ - } /* end if */ - } /* end else */ - } /* end if */ -#endif /* H5_HAVE_PARALLEL */ + lf = f->shared->lf; + + if ( H5FD_is_fphdf5_driver(lf) ) { + + HDassert( info_ptr->protected ); + + info_ptr->protected = FALSE; + + /* + * FIXME: If the metadata is *really* deleted at this point + * (deleted == TRUE), we need to send a request to the SAP + * telling it to remove that bit of metadata from its cache. + */ + if ( H5FP_request_release_lock(H5FD_fphdf5_file_id(lf), addr, + TRUE, &req_id, &status) < 0 ) + HGOTO_ERROR(H5E_FPHDF5, H5E_CANTUNLOCK, FAIL, \ + "can't unlock data on SAP!") + + /* Flush a thing to the SAP */ + if ( thing ) { - /* - * Flush any object already in the cache at that location. It had - * better not be another copy of the protected object. - */ - if (*info) { -#ifdef H5AC_DEBUG - H5AC_subid_t type_id = (*info)->type->id; /* Remember this for later */ -#endif /* H5AC_DEBUG */ + if ( ((H5AC_info_t *)thing)->dirty ) { - assert(H5F_addr_ne((*info)->addr, addr)); - flush = (*info)->type->flush; + if ( type->flush(f, dxpl_id, FALSE, addr, thing) < 0 ) { - if ((flush)(f, dxpl_id, TRUE, (*info)->addr, (*info)) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush object") + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush object") + } + } -#ifdef H5AC_DEBUG - ++cache->diagnostics[type_id].nflushes; -#endif /* H5AC_DEBUG */ + /* Always clear/delete the object from the local cache */ + if ( type->clear(f, thing, TRUE) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, \ + "unable to free object") + + } + } + + /* Exit now. The FPHDF5 stuff is finished. */ + HGOTO_DONE(SUCCEED); } + } +#endif /* H5_HAVE_FPHDF5 */ +#endif /* H5_HAVE_PARALLEL */ - /* Insert the object back into the cache; it is no longer protected. */ - (*info) = thing; - (*info)->type = type; - (*info)->addr = addr; - } /* end if */ - else { - /* Destroy previously cached thing */ - if ((type->clear)(f, thing, TRUE) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "unable to free object") - } /* end else */ + result = H5C_unprotect(f, + dxpl_id, + H5AC_noblock_dxpl_id, + cache_ptr, + type, + addr, + thing, + deleted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "H5C_unprotect() failed.") + } done: - if (ret_value != FAIL) - /* Decrement the number of protected items outstanding */ - --cache->nprots; FUNC_LEAVE_NOAPI(ret_value) -} -#ifdef H5AC_DEBUG +} /* H5AC_unprotect() */ + /*------------------------------------------------------------------------- * Function: H5AC_stats @@ -1727,75 +1194,122 @@ done: * 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(). * *------------------------------------------------------------------------- */ + herr_t H5AC_stats(H5F_t UNUSED *f) { - H5AC_subid_t i; - char s[32], ascii[32]; - H5AC_t *cache = f->shared->cache; - double miss_rate; - herr_t ret_value=SUCCEED; /* Return value */ + herr_t ret_value = SUCCEED; /* Return value */ + H5AC_t * cache_ptr; FUNC_ENTER_NOAPI(H5AC_stats, FAIL) - if (H5DEBUG(AC)) { - fprintf(H5DEBUG(AC), "H5AC: meta data cache statistics for file %s\n", - f->name); - fprintf(H5DEBUG(AC), " %-18s %8s %8s %8s %8s+%-8s\n", - "Layer", "Hits", "Misses", "MissRate", "Inits", "Flushes"); - fprintf(H5DEBUG(AC), " %-18s %8s %8s %8s %8s-%-8s\n", - "-----", "----", "------", "--------", "-----", "-------"); - - for (i = H5AC_BT_ID; i < H5AC_NTYPES; i++) { - - switch (i) { - case H5AC_BT_ID: - HDstrcpy(s, "B-tree nodes"); - break; - case H5AC_SNODE_ID: - HDstrcpy(s, "symbol table nodes"); - break; - case H5AC_LHEAP_ID: - HDstrcpy (s, "local heaps"); - break; - case H5AC_GHEAP_ID: - HDstrcpy (s, "global heaps"); - break; - case H5AC_OHDR_ID: - HDstrcpy(s, "object headers"); - break; - default: - sprintf(s, "unknown id %d", i); - } - - if (cache->diagnostics[i].nhits>0 || - cache->diagnostics[i].nmisses>0) { - miss_rate = 100.0 * cache->diagnostics[i].nmisses / - (cache->diagnostics[i].nhits+ - cache->diagnostics[i].nmisses); - } else { - miss_rate = 0.0; - } - - if (miss_rate > 100) { - sprintf(ascii, "%7d%%", (int) (miss_rate + 0.5)); - } else { - sprintf(ascii, "%7.2f%%", miss_rate); - } - fprintf(H5DEBUG(AC), " %-18s %8u %8u %7s %8u%+-9ld\n", s, - cache->diagnostics[i].nhits, - cache->diagnostics[i].nmisses, - ascii, - cache->diagnostics[i].ninits, - ((long)(cache->diagnostics[i].nflushes) - - (long)(cache->diagnostics[i].ninits))); - } + HDassert(f); + HDassert(f->shared->cache); + + cache_ptr = (H5AC_t *)(f->shared->cache); + + H5C_stats(cache_ptr, f->name, FALSE); /* at present, this can't fail */ + +done: + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5AC_stats() */ + + +/*************************************************************************/ +/**************************** Private Functions: *************************/ +/*************************************************************************/ + +/*------------------------------------------------------------------------- + * + * Function: H5AC_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: + * + *------------------------------------------------------------------------- + */ + +#ifdef H5_HAVE_PARALLEL +static herr_t +H5AC_check_if_write_permitted(H5F_t *f, + hid_t dxpl_id, + hbool_t * write_permitted_ptr) +#else /* H5_HAVE_PARALLEL */ +static herr_t +H5AC_check_if_write_permitted(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 */ + + FUNC_ENTER_NOAPI(H5AC_check_if_write_permitted, FAIL) + +#ifdef H5_HAVE_PARALLEL + + if ( IS_H5FD_MPI(f) ) { + + H5P_genplist_t *dxpl; /* Dataset transfer property list */ + H5FD_mpio_xfer_t xfer_mode; /* I/O transfer 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") + + } + + if ( xfer_mode == H5FD_MPIO_INDEPENDENT ) { + + write_permitted = FALSE; + + } else { + + HDassert(xfer_mode == H5FD_MPIO_COLLECTIVE ); + + } } +#endif /* H5_HAVE_PARALLEL */ + + *write_permitted_ptr = write_permitted; + done: + FUNC_LEAVE_NOAPI(ret_value) -} -#endif /* H5AC_DEBUG */ + +} /* H5AC_check_if_write_permitted() */ + diff --git a/src/H5ACprivate.h b/src/H5ACprivate.h index f12f397..6acc3e3 100644 --- a/src/H5ACprivate.h +++ b/src/H5ACprivate.h @@ -21,7 +21,9 @@ * Purpose: Constants and typedefs available to the rest of the * library. * - * Modifications: + * Modifications: JRM - 6/4/04 + * Complete re-write for a new caching algorithm + * located in H5C.c * *------------------------------------------------------------------------- */ @@ -29,21 +31,20 @@ #ifndef _H5ACprivate_H #define _H5ACprivate_H -#include "H5ACpublic.h" /*public prototypes */ +#include "H5ACpublic.h" /*public prototypes */ /* Pivate headers needed by this header */ #include "H5private.h" /* Generic Functions */ #include "H5Fprivate.h" /* File access */ +#include "H5Cprivate.h" /* cache */ -/* - * Feature: Define H5AC_DEBUG on the compiler command line if you want to - * debug H5AC_protect() and H5AC_unprotect() by insuring that - * nothing accesses protected objects. NDEBUG must not be defined - * in order for this to have any effect. - */ -#ifdef NDEBUG -# undef H5AC_DEBUG -#endif + +#define H5AC_BT_ID 0 /*B-tree nodes */ +#define H5AC_SNODE_ID 1 /*symbol table nodes */ +#define H5AC_LHEAP_ID 2 /*local heap */ +#define H5AC_GHEAP_ID 3 /*global heap */ +#define H5AC_OHDR_ID 4 /*object header */ +#define H5AC_NTYPES 5 /* * Class methods pertaining to caching. Each type of cached object will @@ -64,28 +65,38 @@ * 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. + */ + +typedef H5C_load_func_t H5AC_load_func_t; +typedef H5C_flush_func_t H5AC_flush_func_t; +typedef H5C_dest_func_t H5AC_dest_func_t; +typedef H5C_clear_func_t H5AC_clear_func_t; +typedef H5C_size_func_t H5AC_size_func_t; + +typedef H5C_class_t H5AC_class_t; + + +/* The H5AC_NSLOTS #define is now obsolete, as the metadata cache no longer + * uses slots. However I am leaving it in for now to avoid modifying the + * interface between the metadata cache and the rest of HDF. It should + * be removed when we get to dealing with the size_hint parameter in + * H5AC_create(). + * JRM - 5/20/04 + * + * Old comment on H5AC_NSLOTS follows: + * + * A cache has a certain number of entries. Objects are mapped into a + * cache entry by hashing the object's file address. Each file has its + * own cache, an array of slots. */ -typedef enum H5AC_subid_t { - H5AC_BT_ID = 0, /*B-tree nodes */ - H5AC_SNODE_ID = 1, /*symbol table nodes */ - H5AC_LHEAP_ID = 2, /*local heap */ - H5AC_GHEAP_ID = 3, /*global heap */ - H5AC_OHDR_ID = 4, /*object header */ - H5AC_NTYPES = 5 /*THIS MUST BE LAST! */ -} H5AC_subid_t; - -typedef void *(*H5AC_load_func_t)(H5F_t *f, hid_t dxpl_id, haddr_t addr, const void *udata1, void *udata2); -typedef herr_t (*H5AC_flush_func_t)(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, void *thing); -typedef herr_t (*H5AC_dest_func_t)(H5F_t *f, void *thing); -typedef herr_t (*H5AC_clear_func_t)(H5F_t *f, void *thing, hbool_t dest); - -typedef struct H5AC_class_t { - H5AC_subid_t id; - H5AC_load_func_t load; - H5AC_flush_func_t flush; - H5AC_dest_func_t dest; - H5AC_clear_func_t clear; -} H5AC_class_t; +#define H5AC_NSLOTS 10330 /* The library "likes" this number... */ + + +typedef H5C_cache_entry_t H5AC_info_t; + /*===----------------------------------------------------------------------=== * Protect Types @@ -95,27 +106,15 @@ typedef struct H5AC_class_t { * type of operation you're planning on doing to the metadata. The * Flexible Parallel HDF5 locking can then act accordingly. */ + typedef enum H5AC_protect_t { H5AC_WRITE, /* Protect object for writing */ H5AC_READ /* Protect object for reading */ } H5AC_protect_t; -/* - * A cache has a certain number of entries. Objects are mapped into a - * cache entry by hashing the object's file address. Each file has its - * own cache, an array of slots. - */ -#define H5AC_NSLOTS 10330 /* The library "likes" this number... */ -typedef struct H5AC_info_t { - const H5AC_class_t *type; /*type of object stored here */ - haddr_t addr; /*file address for object */ - hbool_t dirty; /* 'Dirty' flag for cached object */ -} H5AC_info_t; -typedef H5AC_info_t *H5AC_info_ptr_t; /* Typedef for free lists */ - -/* Typedef for metadata cache (defined in H5AC.c) */ -typedef struct H5AC_t H5AC_t; +/* Typedef for metadata cache (defined in H5C.c) */ +typedef H5C_t H5AC_t; /* Metadata specific properties for FAPL */ /* (Only used for parallel I/O) */ @@ -158,9 +157,9 @@ H5_DLL herr_t H5AC_flush(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, hadd H5_DLL herr_t H5AC_rename(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, haddr_t old_addr, haddr_t new_addr); H5_DLL herr_t H5AC_dest(H5F_t *f, hid_t dxpl_id); -#ifdef H5AC_DEBUG + H5_DLL herr_t H5AC_stats(H5F_t *f); -#endif /* H5AC_DEBUG */ + #endif /* !_H5ACprivate_H */ @@ -156,6 +156,7 @@ static H5B_t *H5B_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, const void *_type, static herr_t H5B_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5B_t *b); static herr_t H5B_dest(H5F_t *f, H5B_t *b); static herr_t H5B_clear(H5F_t *f, H5B_t *b, hbool_t destroy); +static herr_t H5B_compute_size(H5F_t *f, H5B_t *bt, size_t *size_ptr); /* H5B inherits cache-like properties from H5AC */ static const H5AC_class_t H5AC_BT[1] = {{ @@ -164,6 +165,7 @@ static const H5AC_class_t H5AC_BT[1] = {{ (H5AC_flush_func_t)H5B_flush, (H5AC_dest_func_t)H5B_dest, (H5AC_clear_func_t)H5B_clear, + (H5AC_size_func_t)H5B_compute_size, }}; /* Declare a free list to manage the page information */ @@ -630,6 +632,56 @@ done: /*------------------------------------------------------------------------- + * Function: H5B_compute_size + * + * Purpose: Compute the size in bytes of the specified instance of + * H5B_t on disk, and return it in *len_ptr. On failure, + * the value of *len_ptr is undefined. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/13/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +H5B_compute_size(H5F_t *f, H5B_t *bt, size_t *size_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + size_t size; + + FUNC_ENTER_NOAPI(H5B_compute_size, FAIL) + + /* check arguments */ + HDassert(f); + HDassert(bt); + HDassert(bt->type); + HDassert(size_ptr); + + size = H5B_nodesize(f, bt->type, NULL, bt->sizeof_rkey); + + if ( size == 0 ) { + + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, FAIL, \ + "H5B_nodesize() failed"); + + } else { + + *size_ptr = size; + + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5B_H5B_compute_size() */ + + +/*------------------------------------------------------------------------- * Function: H5B_find * * Purpose: Locate the specified information in a B-tree and return diff --git a/src/H5C.c b/src/H5C.c new file mode 100644 index 0000000..c16f95f --- /dev/null +++ b/src/H5C.c @@ -0,0 +1,3490 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*------------------------------------------------------------------------- + * + * Created: H5C.c + * June 1 2004 + * John Mainzer + * + * Purpose: Functions in this file implement a generic cache for + * things which exist on disk, and which may be + * unambiguously referenced by their disk addresses. + * + * The code in this module was initially written in + * support of a complete re-write of the metadata cache + * in H5AC.c However, other uses for the cache code + * suggested themselves, and thus this file was created + * in an attempt to support re-use. + * + * For a detailed overview of the cache, please see the + * header comment for H5C_t in this file. + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +/************************************************************************** + * + * To Do: + * + * Code Changes: + * + * - Remove extra functionality in H5C_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 + * H5C_make_space_in_cache(). + * + * - Also in H5C_make_space_in_cache(), use high and low water marks + * to reduce the number of I/O calls. + * + * - When flushing, attempt to combine contiguous entries to reduce + * I/O overhead. Can't do this just yet as some entries are not + * contiguous. Do this in parallel only or in serial as well? + * + * - Create MPI type for dirty objects when flushing in parallel. + * + * Tests: + * + * - Trim execution time. + * + * - Add random tests. + * + **************************************************************************/ + +#define H5F_PACKAGE /*suppress error about including H5Fpkg */ + +/* Pablo information */ +/* (Put before include files to avoid problems with inline functions) */ +#define PABLO_MASK H5C_mask + +#include "H5private.h" /* Generic Functions */ +#include "H5Cprivate.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 "H5TBprivate.h" /* Threaded, Balanced, Binary Trees */ + + +/* Interface initialization -- disabled in this case */ +#if 0 +static int interface_initialize_g = 0; +#endif +#define INTERFACE_INIT NULL + + +/**************************************************************************** + * + * We maintain doubly linked lists of instances of H5C_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 H5C_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 H5C_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 + * + ****************************************************************************/ + +#if H5C_DO_SANITY_CHECKS + +#define H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ +if ( ( (head_ptr) == NULL ) || \ + ( (tail_ptr) == NULL ) || \ + ( (entry_ptr) == NULL ) || \ + ( (len) <= 0 ) || \ + ( (Size) < (entry_ptr)->size ) || \ + ( ( (Size) == (entry_ptr)->size ) && ( (len) != 1 ) ) || \ + ( ( (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, FAIL, "DLL pre remove SC failed") \ +} + +#define H5C__DLL_SC(head_ptr, tail_ptr, len, Size) \ +if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ + ( (head_ptr) != (tail_ptr) ) \ + ) || \ + ( (len) < 0 ) || \ + ( (Size) < 0 ) || \ + ( ( (len) == 1 ) && \ + ( ( (head_ptr) != (tail_ptr) ) || ( (cache_ptr)->size <= 0 ) || \ + ( (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, FAIL, "DLL sanity check failed") \ +} + +#define H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ +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) ) || ( (Size) <= 0 ) || \ + ( (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, FAIL, "DLL pre insert SC failed") \ +} + +#else /* H5C_DO_SANITY_CHECKS */ + +#define H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) +#define H5C__DLL_SC(head_ptr, tail_ptr, len, Size) +#define H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) + +#endif /* H5C_DO_SANITY_CHECKS */ + + +#define H5C__DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size) \ + H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ + 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 H5C__DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size) \ + H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ + 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 H5C__DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size) \ + H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ + { \ + 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; \ + } + + +#if H5C_DO_SANITY_CHECKS + +#define H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ +if ( ( (head_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 ) && ( (head_ptr) != (entry_ptr) ) ) || \ + ( ( (entry_ptr)->aux_next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \ + ( ( (len) == 1 ) && \ + ( ! ( ( (head_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, FAIL, "aux DLL pre remove SC failed") \ +} + +#define H5C__AUX_DLL_SC(head_ptr, tail_ptr, len, Size) \ +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, FAIL, "AUX DLL sanity check failed") \ +} + +#define H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ +if ( ( (entry_ptr) == NULL ) || \ + ( (entry_ptr)->aux_next != NULL ) || \ + ( (entry_ptr)->aux_prev != NULL ) || \ + ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ + ( (head_ptr) != (tail_ptr) ) \ + ) || \ + ( (len) < 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, FAIL, "AUX DLL pre insert SC failed") \ +} + +#else /* H5C_DO_SANITY_CHECKS */ + +#define H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) +#define H5C__AUX_DLL_SC(head_ptr, tail_ptr, len, Size) +#define H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) + +#endif /* H5C_DO_SANITY_CHECKS */ + + +#define H5C__AUX_DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size) \ + H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ + 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 H5C__AUX_DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size) \ + H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ + 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 H5C__AUX_DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size) \ + H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ + { \ + 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 encabled, and evaluate to the empty string when it is not. + * + ***********************************************************************/ + +#if H5C_COLLECT_CACHE_STATS + +#define H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) \ + (((cache_ptr)->insertions)[(entry_ptr)->type->id])++; \ + 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; + +#define H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) \ + (((cache_ptr)->renames)[(entry_ptr)->type->id])++; + +#if H5C_COLLECT_CACHE_ENTRY_STATS + +#define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) \ + (entry_ptr)->accesses = 0; \ + (entry_ptr)->clears = 0; \ + (entry_ptr)->flushes = 0; + +#define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) \ + (((cache_ptr)->clears)[(entry_ptr)->type->id])++; \ + ((entry_ptr)->clears)++; + +#define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) \ + (((cache_ptr)->flushes)[(entry_ptr)->type->id])++; \ + ((entry_ptr)->flushes)++; + +#define H5C__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; \ + } \ + +#define H5C__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 ( (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; \ + ((entry_ptr)->accesses)++; + +#else /* H5C_COLLECT_CACHE_ENTRY_STATS */ + +#define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) + +#define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) \ + (((cache_ptr)->clears)[(entry_ptr)->type->id])++; + +#define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) \ + (((cache_ptr)->flushes)[(entry_ptr)->type->id])++; + +#define H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) \ + (((cache_ptr)->evictions)[(entry_ptr)->type->id])++; + +#define H5C__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 ( (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; + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + +#else /* H5C_COLLECT_CACHE_STATS */ + +#define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) +#define H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) + +#endif /* H5C_COLLECT_CACHE_STATS */ + + +/**************************************************************************** + * + * structure H5C_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 + * + * magic: Unsigned 32 bit integer always set to H5C__H5C_T_MAGIC. This + * field is used to validate pointers to instances of H5C_t. + * + * 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. + * + * 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 will presume that writes are always permissable. + * + * + * The cache requires an index to facilitate searching for entries. The + * following fields support that index. + * + * index_len: Number of entries currently in the threaded binary B-tree + * used to index the cache. + * + * index_size: Number of bytes of cache entries currently stored in the + * threaded binary B-tree 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_tree_ptr: pointer to the instance of H5TB_TREE used to index + * the cache. I use an instance of H5TB_TREE instead of + * a more conventional hash table based design for two + * reasons: + * + * a) the code is already present and tested. + * + * b) the H5TB_TREE makes it easy to check for adjacent + * cache entries so that writes can be combined and + * thus optimized. + * + * If time permitted, a more efficient index could be + * constructed. However, this should do for now. If the + * additional lookup overhead proves excessive, I will + * write specialized code. + * + * + * 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. + * + * + * 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. + * + * + * Statistics collection fields: + * + * When enabled, these fields are used to collect statistics as described + * below. The first set are collected only when H5C_COLLECT_CACHE_STATS + * is true. + * + * hits: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. 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 H5C__MAX_NUM_TYPE_IDS. 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. + * + * insertions: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. 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. + * + * clears: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. 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 H5C__MAX_NUM_TYPE_IDS. 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 H5C__MAX_NUM_TYPE_IDS. 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 H5C__MAX_NUM_TYPE_IDS. 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. + * + * 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_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. + * + * The remaining stats are collected only when both H5C_COLLECT_CACHE_STATS + * and H5C_COLLECT_CACHE_ENTRY_STATS are true. + * + * max_accesses: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. 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 H5C__MAX_NUM_TYPE_IDS. 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 H5C__MAX_NUM_TYPE_IDS. 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 H5C__MAX_NUM_TYPE_IDS. 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. + * + * + * 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. + * + ****************************************************************************/ + +#define H5C__H5C_T_MAGIC 0x005CAC0E +#define H5C__MAX_NUM_TYPE_IDS 9 + +struct H5C_t +{ + uint32_t magic; + + int32_t max_type_id; + const char * (* type_name_table_ptr)[]; + + size_t max_cache_size; + size_t min_clean_size; + + H5C_write_permitted_func_t check_write_permitted; + + int32_t index_len; + size_t index_size; + H5TB_TREE * index_tree_ptr; + + int32_t pl_len; + size_t pl_size; + H5C_cache_entry_t * pl_head_ptr; + H5C_cache_entry_t * pl_tail_ptr; + + int32_t LRU_list_len; + size_t LRU_list_size; + H5C_cache_entry_t * LRU_head_ptr; + H5C_cache_entry_t * LRU_tail_ptr; + + int32_t cLRU_list_len; + size_t cLRU_list_size; + H5C_cache_entry_t * cLRU_head_ptr; + H5C_cache_entry_t * cLRU_tail_ptr; + + int32_t dLRU_list_len; + size_t dLRU_list_size; + H5C_cache_entry_t * dLRU_head_ptr; + H5C_cache_entry_t * dLRU_tail_ptr; + +#if H5C_COLLECT_CACHE_STATS + + /* stats fields */ + int64_t hits[H5C__MAX_NUM_TYPE_IDS]; + int64_t misses[H5C__MAX_NUM_TYPE_IDS]; + int64_t insertions[H5C__MAX_NUM_TYPE_IDS]; + int64_t clears[H5C__MAX_NUM_TYPE_IDS]; + int64_t flushes[H5C__MAX_NUM_TYPE_IDS]; + int64_t evictions[H5C__MAX_NUM_TYPE_IDS]; + int64_t renames[H5C__MAX_NUM_TYPE_IDS]; + + int32_t max_index_len; + size_t max_index_size; + + int32_t max_pl_len; + size_t max_pl_size; + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + int32_t max_accesses[H5C__MAX_NUM_TYPE_IDS]; + int32_t min_accesses[H5C__MAX_NUM_TYPE_IDS]; + int32_t max_clears[H5C__MAX_NUM_TYPE_IDS]; + int32_t max_flushes[H5C__MAX_NUM_TYPE_IDS]; + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + +#endif /* H5C_COLLECT_CACHE_STATS */ + + hbool_t skip_file_checks; + hbool_t skip_dxpl_id_checks; + +}; + + +/* + * Private file-scope variables. + */ + +/* Declare a free list to manage the H5C_t struct */ +H5FL_DEFINE_STATIC(H5C_t); + +/* + * Private file-scope function declarations: + */ + +static herr_t H5C_flush_single_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type_ptr, + haddr_t addr, + unsigned flags, + H5TB_NODE * tgt_node_ptr, + hbool_t * first_flush_ptr, + hbool_t remove_entry_from_tree_on_destroy); + +static herr_t H5C_insert_entry_in_tree(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr); + +static void * H5C_load_entry(H5F_t * f, + hid_t dxpl_id, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2, + hbool_t skip_file_checks); + +static herr_t H5C_make_space_in_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + size_t space_needed, + hbool_t write_permitted); + +static herr_t H5C_remove_entry_from_tree(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr, + H5TB_NODE * node_ptr); + +static herr_t H5C_update_rp_for_eviction(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr); + +static herr_t H5C_update_rp_for_flush(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr); + +static herr_t H5C_update_rp_for_insertion(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr); + +static herr_t H5C_update_rp_for_protect(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr); + +static herr_t H5C_update_rp_for_rename(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr); + +static herr_t H5C_update_rp_for_unprotect(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr); + + +/*------------------------------------------------------------------------- + * Function: H5C_create + * + * Purpose: Allocate, initialize, and return the address of a new + * instance of H5C_t. + * + * In general, the max_cache_size parameter must be positive, + * and the min_clean_size parameter must lie in the closed + * interval [0, max_cache_size]. + * + * The check_write_permitted parameter must either be NULL, + * or point to a function of type H5C_write_permitted_func_t. + * If it is NULL, the cache will presume that writes are + * always permitted. + * + * Return: Success: Pointer to the new instance. + * + * Failure: NULL + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +H5C_t * +H5C_create(size_t max_cache_size, + size_t min_clean_size, + int max_type_id, + const char * (* type_name_table_ptr)[], + H5C_write_permitted_func_t check_write_permitted) +{ + int i; + H5C_t * cache_ptr = NULL; + H5C_t * ret_value = NULL; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_create, NULL) + + HDassert( max_cache_size > 0 ); + HDassert( min_clean_size <= max_cache_size ); + + HDassert( max_type_id >= 0 ); + HDassert( max_type_id < H5C__MAX_NUM_TYPE_IDS ); + HDassert( type_name_table_ptr ); + + for ( i = 0; i <= max_type_id; i++ ) { + + HDassert( (*type_name_table_ptr)[i] ); + HDassert( strlen((* type_name_table_ptr)[i]) > 0 ); + } + + + if ( NULL == (cache_ptr = H5FL_CALLOC(H5C_t)) ) { + + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, \ + "memory allocation failed") + } + + if ( (cache_ptr->index_tree_ptr = H5TB_fast_dmake(H5TB_FAST_HADDR_COMPARE)) + == NULL ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, NULL, "can't create TBBT.") + } + + /* If we get this far, we should succeed. Go ahead and initialize all + * the fields. + */ + + cache_ptr->magic = H5C__H5C_T_MAGIC; + + 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->index_len = 0; + cache_ptr->index_size = (size_t)0; + + 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->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; + + H5C_stats__reset(cache_ptr); + + cache_ptr->skip_file_checks = FALSE; + cache_ptr->skip_dxpl_id_checks = FALSE; + + /* Set return value */ + ret_value = cache_ptr; + +done: + + if ( ret_value == 0 ) { + + if ( cache_ptr != NULL ) { + + if ( cache_ptr->index_tree_ptr != NULL ) { + + /* the index tree should be empty, so we can pass in + * NULL for the fd & fk parameters. + */ + H5TB_dfree(cache_ptr->index_tree_ptr, NULL, NULL); + } + + cache_ptr->magic = 0; + H5FL_FREE(H5C_t, cache_ptr); + cache_ptr = NULL; + + } /* end if */ + + } /* end if */ + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_create() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_dest + * + * Purpose: Flush all data to disk and destroy the cache. + * + * This function fails if any object are protected since the + * resulting file might not be consistent. + * + * 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: + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_dest(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr) +{ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_dest, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + + if ( H5C_flush_cache(f, primary_dxpl_id, secondary_dxpl_id, + cache_ptr, H5F_FLUSH_INVALIDATE) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache") + } + + if ( cache_ptr->index_tree_ptr != NULL ) { + + /* the index tree should be empty, so we can pass in + * NULL for the fd & fk parameters. + */ + H5TB_dfree(cache_ptr->index_tree_ptr, NULL, NULL); + cache_ptr->index_tree_ptr = NULL; + } + + cache_ptr->magic = 0; + + H5FL_FREE(H5C_t, cache_ptr); + +done: + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_dest() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_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: + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_dest_empty(H5C_t * cache_ptr) +{ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_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 != H5C__H5C_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->index_tree_ptr != NULL ) { + + /* the index tree should be empty, so we can pass in + * NULL for the fd & fk parameters. + */ + H5TB_dfree(cache_ptr->index_tree_ptr, NULL, NULL); + cache_ptr->index_tree_ptr = NULL; + } + + cache_ptr->magic = 0; + + H5FL_FREE(H5C_t, cache_ptr); + +done: + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_dest_empty() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_flush_cache + * + * Purpose: Flush (and possibly destroy) the entries contained in the + * specified cache. + * + * If the cache contains protected entries, the function will + * fail, as protected entries cannot be flushed. However + * all unprotected entries should be flushed before the + * function returns failure. + * + * 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: + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_flush_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + unsigned flags) +{ + herr_t status; + herr_t ret_value = SUCCEED; + hbool_t destroy = ( (flags & H5F_FLUSH_INVALIDATE) != 0 ); + hbool_t first_flush = TRUE; + int32_t protected_entries = 0; + H5TB_NODE * node_ptr; + H5C_cache_entry_t * entry_ptr; +#if H5C_DO_SANITY_CHECKS + int32_t actual_index_len = 0; + size_t actual_index_size = 0; +#endif /* H5C_DO_SANITY_CHECKS */ + + FUNC_ENTER_NOAPI(H5C_flush_cache, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + + + if ( cache_ptr->index_tree_ptr->root == NULL ) { + + node_ptr = NULL; + HDassert( cache_ptr->index_len == 0 ); + HDassert( cache_ptr->index_size == 0 ); + + } else { + + node_ptr = H5TB_first(cache_ptr->index_tree_ptr->root); + } + + while ( node_ptr != NULL ) + { + entry_ptr = (H5C_cache_entry_t *)(node_ptr->data); + HDassert( entry_ptr != NULL ); + +#if H5C_DO_SANITY_CHECKS + actual_index_len++; + actual_index_size += entry_ptr->size; +#endif /* H5C_DO_SANITY_CHECKS */ + + if ( entry_ptr->protected ) { + + /* we have major problems -- but lets flush everything + * we can before we flag an error. + */ + protected_entries++; + + } else { + + status = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + NULL, + entry_ptr->addr, + flags, + node_ptr, + &first_flush, + 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.") + } + } + + node_ptr = H5TB_next(node_ptr); + + } /* while */ + + HDassert( protected_entries == cache_ptr->pl_len ); + +#if H5C_DO_SANITY_CHECKS + HDassert( actual_index_len == cache_ptr->index_len ); + HDassert( actual_index_size == cache_ptr->index_size ); +#endif /* H5C_DO_SANITY_CHECKS */ + + if ( destroy ) { + + /* don't pass in any key or data free functions, as all + * unprotected entries should have already been destroyed. + */ + H5TB_free(&(cache_ptr->index_tree_ptr->root), NULL, NULL); + cache_ptr->index_len = 0; + cache_ptr->index_size = 0; + + if ( protected_entries > 0 ) + { + /* the caller asked us to flush and destroy a cache that + * contains one or more protected entries. Since we can't + * flush protected entries, we haven't destroyed them either. + * Since they are all on the protected list, just re-insert + * them into the tree before we flag an error. + */ + entry_ptr = cache_ptr->pl_head_ptr; + + while ( entry_ptr != NULL ) + { + if ( H5C_insert_entry_in_tree(cache_ptr, entry_ptr) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Can't re-insert protected entry.") + } + entry_ptr = entry_ptr->next; + } + } + } + + if ( protected_entries > 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, "cache has protected items") + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_flush_cache() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_insert_entry + * + * Purpose: Adds the specified thing to the cache. The thing need not + * exist on disk yet, but it must have an address and disk + * space reserved. + * + * 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: + * + *------------------------------------------------------------------------- + */ + +herr_t +H5C_insert_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + void * thing) +{ + herr_t result; + herr_t ret_value = SUCCEED; /* Return value */ + hbool_t write_permitted = TRUE; + H5C_cache_entry_t * entry_ptr; + H5TB_NODE * node_ptr = NULL; + + FUNC_ENTER_NOAPI(H5C_insert_entry, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( type->flush ); + HDassert( type->size ); + HDassert( H5F_addr_defined(addr) ); + HDassert( thing ); + + entry_ptr = (H5C_cache_entry_t *)thing; + + entry_ptr->addr = addr; + entry_ptr->type = type; + + if ( (type->size)(f, thing, &(entry_ptr->size)) < 0 ) { + + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, FAIL, \ + "Can't get size of thing") + } + + HDassert( entry_ptr->size < H5C_MAX_ENTRY_SIZE ); + + entry_ptr->next = NULL; + entry_ptr->prev = NULL; + entry_ptr->aux_next = NULL; + entry_ptr->aux_prev = NULL; + + H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) + + if ( (cache_ptr->index_size + entry_ptr->size) > cache_ptr->max_cache_size ){ + + size_t space_needed; + + if ( cache_ptr->check_write_permitted != NULL ) { + + result = (cache_ptr->check_write_permitted)(f, + primary_dxpl_id, + &write_permitted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "Can't get write_permitted") + } + } + + HDassert( entry_ptr->size <= H5C_MAX_ENTRY_SIZE ); + + space_needed = (cache_ptr->index_size + entry_ptr->size) - + cache_ptr->max_cache_size; + + /* It would be nice to be able to do a tight sanity check on + * space_needed here, but it is hard to assign an upper bound on + * its value other than then value assigned to it. + * + * This fact springs from several features of the cache: + * + * 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 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. + * + * In any case, I hope this explains why there is no sanity + * check on space_needed here. + */ + + result = H5C_make_space_in_cache(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + space_needed, + write_permitted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "H5C_make_space_in_cache failed.") + } + } + + /* verify the the new entry isn't already in the tree -- scream + * and die if it is. + */ + node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, entry_ptr, NULL); + + if ( node_ptr != NULL ) { + + if ( node_ptr->key == ((void *)(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->protected = FALSE; + + if ( H5C_insert_entry_in_tree(cache_ptr, entry_ptr) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "Can't insert entry in tree.") + + } + + if ( H5C_update_rp_for_insertion(cache_ptr, entry_ptr) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "Can't update replacement policy for insertion.") + + } + + H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_insert_entry() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_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: + * + *------------------------------------------------------------------------- + */ + +herr_t +H5C_rename_entry(H5F_t * f, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t old_addr, + haddr_t new_addr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + H5TB_NODE * new_node_ptr = NULL; + H5TB_NODE * old_node_ptr = NULL; + H5C_cache_entry_t * entry_ptr; + H5C_cache_entry_t search_target; + + FUNC_ENTER_NOAPI(H5C_rename_entry, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( H5F_addr_defined(old_addr) ); + HDassert( H5F_addr_defined(new_addr) ); + HDassert( H5F_addr_ne(old_addr, new_addr) ); + + search_target.addr = old_addr; + old_node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, + (void *)(&search_target), + NULL); + + if ( ( old_node_ptr == NULL ) || + ( ((H5C_cache_entry_t *)(old_node_ptr->key))->type != type ) ) { + + /* the old item doesn't exist in the cache, so we are done. */ + HGOTO_DONE(SUCCEED) + + } else { + + entry_ptr = old_node_ptr->key; + HDassert( entry_ptr->addr == old_addr ); + HDassert( entry_ptr->type == type ); + HDassert( !(entry_ptr->protected) ); + } + + search_target.addr = new_addr; + new_node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, + (void *)&search_target, + NULL); + + if ( new_node_ptr != NULL ) { /* we are hosed */ + + if ( ((H5C_cache_entry_t *)(new_node_ptr->key))->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 tree, change its address to the new address, 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. + */ + + if ( H5C_remove_entry_from_tree(cache_ptr, entry_ptr, old_node_ptr) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ + "Can't remove entry from tree.") + } + + entry_ptr->addr = new_addr; + + if ( H5C_insert_entry_in_tree(cache_ptr, entry_ptr) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ + "Can't re-insert entry from tree.") + } + + if ( H5C_update_rp_for_rename(cache_ptr, entry_ptr) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ + "Can't can't update replacement policy for a hit.") + } + + H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_rename_entry() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_protect + * + * Purpose: If the target entry is not in the cache, load it. If + * necessary, attempt to evict one or more entries to keep + * the cache within its maximum size. + * + * Mark the target entry as protected, and return its address + * to the caller. The caller must call H5C_unprotect() when + * finished with the entry. + * + * While it is protected, the entry may not be either evicted + * or flushed -- nor may it be accessed by another call to + * H5C_protect. Any attempt to do so will result in a failure. + * + * 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: + * + *------------------------------------------------------------------------- + */ + +void * +H5C_protect(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2) +{ + hbool_t hit = FALSE; + void * thing = NULL; + H5C_cache_entry_t * entry_ptr; + H5TB_NODE * node_ptr = NULL; + void * ret_value; /* Return value */ + H5C_cache_entry_t search_target; + + FUNC_ENTER_NOAPI(H5C_protect, NULL) + + /* check args */ + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( type->flush ); + HDassert( type->load ); + HDassert( H5F_addr_defined(addr) ); + + /* first check to see if the target is in cache */ + search_target.addr = addr; + node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, + (void *)(&search_target), + NULL); + + if ( node_ptr != NULL ) { + + hit = TRUE; + thing = node_ptr->key; + entry_ptr = (H5C_cache_entry_t *)thing; + + } else { /* must try to load the entry from disk. */ + + hit = FALSE; + thing = H5C_load_entry(f, primary_dxpl_id, type, addr, udata1, udata2, + cache_ptr->skip_file_checks); + + if ( thing == NULL ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't load entry") + } + + entry_ptr = (H5C_cache_entry_t *)thing; + + /* try to free up some space if necessay */ + if ( (cache_ptr->index_size + entry_ptr->size) > + cache_ptr->max_cache_size ) { + + hbool_t write_permitted = TRUE; + herr_t result; + size_t space_needed; + + if ( cache_ptr->check_write_permitted != NULL ) { + + result = (cache_ptr->check_write_permitted)(f, + primary_dxpl_id, + &write_permitted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "Can't get write_permitted") + } + } + + HDassert( entry_ptr->size <= H5C_MAX_ENTRY_SIZE ); + + space_needed = (cache_ptr->index_size + entry_ptr->size) - + cache_ptr->max_cache_size; + + /* It would be nice to be able to do a tight sanity check on + * space_needed here, but it is hard to assign an upper bound on + * its value other than then value assigned to it. + * + * This fact springs from several features of the cache: + * + * 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 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. + * + * In any case, I hope this explains why there is no sanity + * check on space_needed here. + */ + + result = H5C_make_space_in_cache(f, primary_dxpl_id, + secondary_dxpl_id, cache_ptr, + space_needed, write_permitted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "H5C_make_space_in_cache failed.") + } + } + + /* insert the entry in the tree and in the protected list. */ + if ( H5C_insert_entry_in_tree(cache_ptr, entry_ptr) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "Can't insert newly loaded entry in tree.") + } + + /* 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. + */ + + if ( H5C_update_rp_for_insertion(cache_ptr, entry_ptr) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "Can't update replacement policy for newly loaded entry.") + + } + } + + HDassert( entry_ptr->addr == addr ); + HDassert( entry_ptr->type == type ); + + if ( entry_ptr->protected ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "Target already protected?!?.") + } + + if ( H5C_update_rp_for_protect(cache_ptr, entry_ptr) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "Can't update replacement policy for protect") + } + + entry_ptr->protected = TRUE; + + ret_value = thing; + + H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_protect() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_unprotect + * + * Purpose: Undo an H5C_protect() call -- specifically, mark the + * entry as unprotected, remove it from the protected list, + * and give it back to the replacement policy. + * + * The TYPE and ADDR arguments must be the same as those in + * the corresponding call to H5C_protect() and the THING + * argument must be the value returned by that call to + * H5C_protect(). + * + * 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, + * H5C_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: + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_unprotect(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + void * thing, + hbool_t deleted) +{ + herr_t ret_value = SUCCEED; /* Return value */ + H5C_cache_entry_t * entry_ptr; + + FUNC_ENTER_NOAPI(H5C_unprotect, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( type->clear ); + HDassert( type->flush ); + HDassert( H5F_addr_defined(addr) ); + HDassert( thing ); + + entry_ptr = (H5C_cache_entry_t *)thing; + + HDassert( entry_ptr->addr == addr ); + HDassert( entry_ptr->type == type ); + + if ( ! (entry_ptr->protected) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "Entry already unprotected??") + } + + if ( H5C_update_rp_for_unprotect(cache_ptr, entry_ptr) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "Can't update replacement policy for unprotect.") + } + + entry_ptr->protected = FALSE; + + /* 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 ) { + + /* the following first flush flag will never be used as we are + * calling H5C_flush_single_entry with both the H5F_FLUSH_CLEAR_ONLY + * and H5F_FLUSH_INVALIDATE flags. However, it is needed for the + * function call. + */ + hbool_t dummy_first_flush = TRUE; + H5TB_NODE * node_ptr; + + /* verify that the target entry is in the tree. */ + + node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, entry_ptr, NULL); + + if ( ( node_ptr == NULL ) || ( node_ptr->key != thing ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "thing not in tree?!?.") + } + + if ( H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + type, + addr, + (H5F_FLUSH_CLEAR_ONLY|H5F_FLUSH_INVALIDATE), + node_ptr, + &dummy_first_flush, + TRUE) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "thing not in tree?!?.") + } + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_unprotect() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_stats + * + * Purpose: Prints statistics about the cache. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +herr_t +H5C_stats(H5C_t * cache_ptr, + const char * cache_name, + hbool_t display_detailed_stats) +{ + herr_t ret_value = SUCCEED; /* Return value */ +#if H5C_COLLECT_CACHE_STATS + int i; + int64_t total_hits = 0; + int64_t total_misses = 0; + int64_t total_insertions = 0; + int64_t total_clears = 0; + int64_t total_flushes = 0; + int64_t total_evictions = 0; + int64_t total_renames = 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; + double hit_rate; +#endif /* H5C_COLLECT_CACHE_STATS */ + + FUNC_ENTER_NOAPI(H5C_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 != H5C__H5C_T_MAGIC ) || + ( !cache_name ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr or cache_name") + } + +#if H5C_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_insertions += cache_ptr->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]; +#if H5C_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]; +#endif /* H5C_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; + } + + HDfprintf(stdout, "\nH5C: cache statistics for %s\n", + cache_name); + + HDfprintf(stdout, "\n"); + + HDfprintf(stdout, + " current (max) index size / length = %ld (%ld) / %ld (%ld)\n", + (long)(cache_ptr->index_size), + (long)(cache_ptr->max_index_size), + (long)(cache_ptr->index_len), + (long)(cache_ptr->max_index_len)); + + HDfprintf(stdout, + " current (max) PL size / length = %ld (%ld) / %ld (%ld)\n", + (long)(cache_ptr->pl_size), + (long)(cache_ptr->max_pl_size), + (long)(cache_ptr->pl_len), + (long)(cache_ptr->max_pl_len)); + + HDfprintf(stdout, + " current LRU list size / length = %ld / %ld\n", + (long)(cache_ptr->LRU_list_size), + (long)(cache_ptr->LRU_list_len)); + + HDfprintf(stdout, + " current clean LRU size / length = %ld / %ld\n", + (long)(cache_ptr->cLRU_list_size), + (long)(cache_ptr->cLRU_list_len)); + + HDfprintf(stdout, + " current dirty LRU size / length = %ld / %ld\n", + (long)(cache_ptr->dLRU_list_size), + (long)(cache_ptr->dLRU_list_len)); + + HDfprintf(stdout, + " Total hits / misses / hit_rate = %ld / %ld / %f\n", + (long)total_hits, + (long)total_misses, + hit_rate); + + HDfprintf(stdout, + " Total clears / flushes / evictions = %ld / %ld / %ld\n", + (long)total_clears, + (long)total_flushes, + (long)total_evictions); + + HDfprintf(stdout, " Total insertions / renames = %ld / %ld\n", + (long)total_insertions, + (long)total_renames); + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + HDfprintf(stdout, " aggregate max / min accesses = %d / %d\n", + (int)aggregate_max_accesses, + (int)aggregate_min_accesses); + + HDfprintf(stdout, " aggregate max_clears / max_flushes = %d / %d\n", + (int)aggregate_max_clears, + (int)aggregate_max_flushes); + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + + if ( display_detailed_stats ) + { + + for ( i = 0; i <= cache_ptr->max_type_id; i++ ) { + + HDfprintf(stdout, "\n"); + + HDfprintf(stdout, " Stats on %s:\n", + (*(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, + " hits / misses / hit_rate = %ld / %ld / %f\n", + (long)(cache_ptr->hits[i]), + (long)(cache_ptr->misses[i]), + hit_rate); + + HDfprintf(stdout, + " clears / flushes / evictions = %ld / %ld / %ld\n", + (long)(cache_ptr->clears[i]), + (long)(cache_ptr->flushes[i]), + (long)(cache_ptr->evictions[i])); + + HDfprintf(stdout, + " insertions / renames = %ld / %ld\n", + (long)(cache_ptr->insertions[i]), + (long)(cache_ptr->renames[i])); + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + HDfprintf(stdout, + " entry max / min accesses = %d / %d\n", + cache_ptr->max_accesses[i], + cache_ptr->min_accesses[i]); + + HDfprintf(stdout, + " entry max_clears / max_flushes = %d / %d\n", + cache_ptr->max_clears[i], + cache_ptr->max_flushes[i]); + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + + } + } + + HDfprintf(stdout, "\n"); + +#endif /* H5C_COLLECT_CACHE_STATS */ + +done: + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_stats() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_stats__reset + * + * Purpose: Reset the stats fields to their initial values. + * + * Return: void + * + * Programmer: John Mainzer, 4/28/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +void +H5C_stats__reset(H5C_t * cache_ptr) +{ + int i; + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + +#if H5C_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->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->max_index_len = 0; + cache_ptr->max_index_size = (size_t)0; + + cache_ptr->max_pl_len = 0; + cache_ptr->max_pl_size = (size_t)0; + +#if H5C_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; + } + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ +#endif /* H5C_COLLECT_CACHE_STATS */ + + return; + +} /* H5C_stats__reset() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_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 +H5C_set_skip_flags(H5C_t * cache_ptr, + hbool_t skip_file_checks, + hbool_t skip_dxpl_id_checks) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_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 != H5C__H5C_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) + +} /* H5C_set_skip_flags() */ + + +/*************************************************************************/ +/**************************** Private Functions: *************************/ +/*************************************************************************/ + +/*------------------------------------------------------------------------- + * + * Function: H5C_flush_single_entry + * + * Purpose: Flush or clear (and evict if requested) the cache entry + * with the specified address and type. If the type is NULL, + * any unprotected entry at the specified address will be + * flushed (and possibly evicted). + * + * Attempts to flush a protected entry will result in an + * error. + * + * *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 H5F_FLUSH_CLEAR_ONLY 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: + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C_flush_single_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type_ptr, + haddr_t addr, + unsigned flags, + H5TB_NODE * tgt_node_ptr, + hbool_t * first_flush_ptr, + hbool_t remove_entry_from_tree_on_destroy) +{ + hbool_t destroy = ( (flags & H5F_FLUSH_INVALIDATE) != 0 ); + hbool_t clear_only = ( (flags & H5F_FLUSH_CLEAR_ONLY) != 0); + herr_t ret_value = SUCCEED; /* Return value */ + herr_t status; + H5TB_NODE * node_ptr; + H5C_cache_entry_t * entry_ptr = NULL; + H5C_cache_entry_t search_target; + + FUNC_ENTER_NOAPI(H5C_flush_single_entry, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( H5F_addr_defined(addr) ); + HDassert( first_flush_ptr ); + + /* If tgt_node_ptr is NULL, look up the target entry in the tree. + * If it doesn't exist, we are done. + */ + + if ( tgt_node_ptr == NULL ) { + + search_target.addr = addr; + node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, + (void *)&search_target, + NULL); + + } else { + + node_ptr = tgt_node_ptr; + } + + if ( node_ptr != NULL ) { + + entry_ptr = (H5C_cache_entry_t *)(node_ptr->data); + HDassert( entry_ptr != NULL ); + HDassert( entry_ptr->addr == addr ); + HDassert( node_ptr->data == node_ptr->key ); + } + + if ( ( entry_ptr != NULL ) && ( entry_ptr->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 */ + +#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. + * + * There are also cases (testing for instance) where it is convenient + * to pass in dummy dxpl_ids. Since we don't use the dxpl_ids directly, + * this isn't a problem -- but we do have to turn off sanity checks + * involving them. We use cache_ptr->skip_dxpl_id_checks to do this. + */ + if ( ( ! cache_ptr->skip_dxpl_id_checks ) && + ( ! clear_only ) && + ( entry_ptr->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(primary_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 */ + HDassert( xfer_mode == H5FD_MPIO_COLLECTIVE || IS_H5FD_FPHDF5(f) ); + } + +#endif /* NDEBUG */ +#endif /* H5_HAVE_PARALLEL */ + if ( clear_only ) { + H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) + } else { + H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) + } + + if ( destroy ) { + H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) + } + + /* remove entry from tree if asked -- must do this now as the + * callback routines will free the entry if destroy is true. + */ + if ( ( destroy ) && ( remove_entry_from_tree_on_destroy ) ) { + if ( H5C_remove_entry_from_tree(cache_ptr, entry_ptr, node_ptr) + < 0 ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Can't delete entry from tree.") + } + } + + /* Update the replacement policy for the flush or eviction. + * Again, do this now so we don't have to reference freed + * memory in the destroy case. + */ + + if ( destroy ) { /* AKA eviction */ + status = H5C_update_rp_for_eviction(cache_ptr, entry_ptr); + } else { + status = H5C_update_rp_for_flush(cache_ptr, entry_ptr); + } + + if ( status < 0 ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Can't update replacement policy.") + } + + /* Clear the dirty flag only, if requested */ + if ( clear_only ) { + /* Call the callback routine to clear all dirty flags for object */ + if ( (entry_ptr->type->clear)(f, entry_ptr, destroy) < 0 ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "can't clear entry") + } + } else { + + /* Only block for all the processes on the first piece of metadata + */ + + if ( *first_flush_ptr && entry_ptr->dirty ) { + status = (entry_ptr->type->flush)(f, primary_dxpl_id, destroy, + entry_ptr->addr, entry_ptr); + *first_flush_ptr = FALSE; + } else { + status = (entry_ptr->type->flush)(f, secondary_dxpl_id, + destroy, entry_ptr->addr, + entry_ptr); + } + + if ( status < 0 ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } + } + + if ( ! destroy ) { + + HDassert( !(entry_ptr->dirty) ); + } + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_flush_single_entry() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_insert_entry_in_tree + * + * Purpose: Insert the specified instance of H5C_cache_entry_t from the + * index tree in the specified instance of H5C_t. Update + * the associated length and size fields. + * + * Return: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 5/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +H5C_insert_entry_in_tree(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + H5TB_NODE * node_ptr = NULL; + + FUNC_ENTER_NOAPI(H5C_insert_entry_in_tree, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( entry_ptr ); + HDassert( entry_ptr->size > 0 ); + HDassert( H5F_addr_defined(entry_ptr->addr) ); + + /* Don't bother to check if the entry is already in the tree -- if it + * is, H5TB_dins() will fail. + */ + node_ptr = H5TB_dins(cache_ptr->index_tree_ptr, (void *)entry_ptr, NULL); + + if ( node_ptr == NULL ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't insert entry in tree") + + } + + cache_ptr->index_len++; + cache_ptr->index_size += entry_ptr->size; + HDassert( cache_ptr->index_len > 0 ); + HDassert( cache_ptr->index_size > 0 ); + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_insert_entry_in_tree */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_load_entry + * + * Purpose: Attempt to load the entry at the specified disk address + * and with the specified type into memory. If successful. + * return the in memory address of the entry. Return NULL + * on failure. + * + * Note that this function simply loads the entry into + * core. It does not insert it into the cache. + * + * Return: Non-NULL on success / NULL on failure. + * + * Programmer: John Mainzer, 5/18/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void * +H5C_load_entry(H5F_t * f, + hid_t dxpl_id, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2, + hbool_t skip_file_checks) +{ + void * thing = NULL; + void * ret_value = NULL; + H5C_cache_entry_t * entry_ptr = NULL; + + FUNC_ENTER_NOAPI(H5C_load_entry, NULL) + + HDassert( skip_file_checks || f ); + HDassert( type ); + HDassert( type->load ); + HDassert( type->size ); + HDassert( H5F_addr_defined(addr) ); + + if ( NULL == (thing = (type->load)(f, dxpl_id, addr, udata1, udata2)) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "unable to load entry") + + } + + entry_ptr = (H5C_cache_entry_t *)thing; + + HDassert( entry_ptr->dirty == FALSE ); + + entry_ptr->addr = addr; + entry_ptr->type = type; + entry_ptr->protected = FALSE; + + if ( (type->size)(f, thing, &(entry_ptr->size)) < 0 ) { + + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, NULL, \ + "Can't get size of thing") + } + + HDassert( entry_ptr->size < H5C_MAX_ENTRY_SIZE ); + + entry_ptr->next = NULL; + entry_ptr->prev = NULL; + entry_ptr->aux_next = NULL; + entry_ptr->aux_prev = NULL; + + H5C__RESET_CACHE_ENTRY_STATS(entry_ptr); + + ret_value = thing; + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_load_entry() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_make_space_in_cache + * + * Purpose: Attempt to evict cache entries until the index_size + * is at least needed_space below max_cache_size. + * + * In passing, also attempt to bring cLRU_list_size to a + * value greater than min_clean_size. + * + * Depending on circumstances, both of these goals may + * be impossible, as in parallel mode, we must avoid generating + * a write as part of a read (to avoid deadlock in collective + * I/O), and in all cases, it is possible (though hopefully + * highly unlikely) that the protected list may exceed the + * maximum size of the cache. + * + * Thus the function simply does its best, returning success + * unless an error is encountered. + * + * 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: + * + *------------------------------------------------------------------------- + */ + +static herr_t +H5C_make_space_in_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + size_t space_needed, + hbool_t write_permitted) +{ + hbool_t first_flush = TRUE; + herr_t ret_value = SUCCEED; /* Return value */ + herr_t result; + int32_t entries_examined = 0; + int32_t initial_list_len; + H5C_cache_entry_t * entry_ptr; + H5C_cache_entry_t * prev_ptr; + + FUNC_ENTER_NOAPI(H5C_make_space_in_cache, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_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->protected) ); + + prev_ptr = entry_ptr->prev; + + if ( entry_ptr->dirty ) { + + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + (unsigned)0, + NULL, + &first_flush, + FALSE); + } else { + + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + H5F_FLUSH_INVALIDATE, + NULL, + &first_flush, + TRUE); + } + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } + + entry_ptr = prev_ptr; + } + + initial_list_len = cache_ptr->dLRU_list_len; + entry_ptr = cache_ptr->dLRU_tail_ptr; + + while ( ( cache_ptr->cLRU_list_size < cache_ptr->min_clean_size ) && + ( entries_examined <= initial_list_len ) && + ( entry_ptr != NULL ) + ) + { + HDassert( ! (entry_ptr->protected) ); + HDassert( entry_ptr->dirty ); + + prev_ptr = entry_ptr->aux_prev; + + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + (unsigned)0, + NULL, + &first_flush, + FALSE); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } + + entry_ptr = prev_ptr; + } + } else { + + 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->protected) ); + HDassert( ! (entry_ptr->dirty) ); + + prev_ptr = entry_ptr->aux_prev; + + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + H5F_FLUSH_INVALIDATE, + NULL, + &first_flush, + TRUE); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } + + entry_ptr = prev_ptr; + } + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_make_space_in_cache() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_remove_entry_from_tree + * + * Purpose: Remove the specified instance of H5C_cache_entry_t from the + * index tree in the specified instance of H5C_t. Update + * the associated length and size fields. + * + * Return: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 5/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +H5C_remove_entry_from_tree(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr, + H5TB_NODE * node_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_remove_entry_from_tree, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( entry_ptr ); + HDassert( node_ptr ); + HDassert( node_ptr->data == entry_ptr ); + HDassert( entry_ptr->size > 0 ); + + if ( entry_ptr->protected ) + { + /* Attempt to delete a protected entry -- scream and die. */ + HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, \ + "Attempt to delete protected entry") + } + + if ( H5TB_rem(&(cache_ptr->index_tree_ptr->root), node_ptr, NULL) + != entry_ptr ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Can't delete entry from tree.") + } else { + HDassert( cache_ptr->index_len > 0 ); + + cache_ptr->index_len--; + + HDassert( cache_ptr->index_size >= entry_ptr->size ); + + cache_ptr->index_size -= entry_ptr->size; + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_remove_entry_from_tree */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_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: + * + *------------------------------------------------------------------------- + */ + +static herr_t +H5C_update_rp_for_eviction(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_update_rp_for_eviction, FAIL) + +#if H5C_DO_SANITY_CHECKS + + if ( ( cache_ptr == NULL ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( entry_ptr == NULL ) || + ( entry_ptr->protected ) || + ( entry_ptr->size <= 0 ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "General sanity checks failed") + } + +#endif /* H5C_DO_SANITY_CHECKS */ + + + /* modified LRU specific code */ + + /* remove the entry from the LRU list. */ + + H5C__DLL_REMOVE(entry_ptr, cache_ptr->LRU_head_ptr, \ + cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ + cache_ptr->LRU_list_size) + + /* 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->dirty ) { + + H5C__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) + } else { + H5C__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) + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_update_rp_for_eviction() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_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: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 5/6/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +H5C_update_rp_for_flush(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_update_rp_for_flush, FAIL) + +#if H5C_DO_SANITY_CHECKS + if ( ( cache_ptr == NULL ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( entry_ptr == NULL ) || + ( entry_ptr->protected ) || + ( entry_ptr->size <= 0 ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "General sanity checks failed") + } +#endif /* H5C_DO_SANITY_CHECKS */ + + /* modified LRU specific code */ + + /* remove the entry from the LRU list, and re-insert it at the head. */ + + H5C__DLL_REMOVE(entry_ptr, cache_ptr->LRU_head_ptr, \ + cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ + cache_ptr->LRU_list_size) + + H5C__DLL_PREPEND(entry_ptr, cache_ptr->LRU_head_ptr, \ + cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ + cache_ptr->LRU_list_size) + + /* 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->dirty ) { + H5C__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) + } else { + H5C__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) + } + + H5C__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) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_update_rp_for_flush() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_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: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 5/17/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +H5C_update_rp_for_insertion(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_update_rp_for_insertion, FAIL) + +#if H5C_DO_SANITY_CHECKS + if ( ( cache_ptr == NULL ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( entry_ptr == NULL ) || + ( entry_ptr->protected ) || + ( entry_ptr->size <= 0 ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "General sanity checks failed") + } +#endif /* H5C_DO_SANITY_CHECKS */ + + /* modified LRU specific code */ + + /* insert the entry at the head of the LRU list. */ + + H5C__DLL_PREPEND(entry_ptr, cache_ptr->LRU_head_ptr, \ + cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ + cache_ptr->LRU_list_size) + + /* insert the entry at the head of the clean or dirty LRU list as + * appropriate. + */ + + if ( entry_ptr->dirty ) { + H5C__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) + } else { + H5C__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) + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_update_rp_for_insertion() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_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: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 5/17/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +H5C_update_rp_for_protect(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_update_rp_for_protect, FAIL) + +#if H5C_DO_SANITY_CHECKS + if ( ( cache_ptr == NULL ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( entry_ptr == NULL ) || + ( entry_ptr->protected ) || + ( entry_ptr->size <= 0 ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "General sanity checks failed") + } +#endif /* H5C_DO_SANITY_CHECKS */ + + /* modified LRU specific code */ + + /* remove the entry from the LRU list. */ + + H5C__DLL_REMOVE(entry_ptr, cache_ptr->LRU_head_ptr, \ + cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ + cache_ptr->LRU_list_size) + + /* Similarly, remove the entry from the clean or dirty LRU list + * as appropriate. + */ + + if ( entry_ptr->dirty ) { + + H5C__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) + + } else { + + H5C__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) + } + + /* End modified LRU specific code. */ + + + /* Regardless of the replacement policy, now add the entry to the + * protected list. + */ + + H5C__DLL_APPEND(entry_ptr, cache_ptr->pl_head_ptr, cache_ptr->pl_tail_ptr, \ + cache_ptr->pl_len, cache_ptr->pl_size) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_update_rp_for_protect() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_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: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 5/17/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +H5C_update_rp_for_rename(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_update_rp_for_rename, FAIL) + +#if H5C_DO_SANITY_CHECKS + if ( ( cache_ptr == NULL ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( entry_ptr == NULL ) || + ( entry_ptr->protected ) || + ( entry_ptr->size <= 0 ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "General sanity checks failed") + } +#endif /* H5C_DO_SANITY_CHECKS */ + + /* modified LRU specific code */ + + /* remove the entry from the LRU list, and re-insert it at the head. */ + + H5C__DLL_REMOVE(entry_ptr, cache_ptr->LRU_head_ptr, \ + cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ + cache_ptr->LRU_list_size) + + H5C__DLL_PREPEND(entry_ptr, cache_ptr->LRU_head_ptr, \ + cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ + cache_ptr->LRU_list_size) + + /* move the entry to the head of either the clean or dirty LRU list + * as appropriate. + */ + + if ( entry_ptr->dirty ) { + + H5C__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) + + H5C__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) + + } else { + + H5C__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) + + H5C__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) + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_update_rp_for_rename() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_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: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 5/19/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +H5C_update_rp_for_unprotect(H5C_t * cache_ptr, + H5C_cache_entry_t * entry_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_update_rp_for_unprotect, FAIL) + +#if H5C_DO_SANITY_CHECKS + if ( ( cache_ptr == NULL ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( entry_ptr == NULL ) || + ( !(entry_ptr->protected) ) || + ( entry_ptr->size <= 0 ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "General sanity checks failed") + } +#endif /* H5C_DO_SANITY_CHECKS */ + + /* Regardless of the replacement policy, remove the entry from the + * protected list. + */ + H5C__DLL_REMOVE(entry_ptr, cache_ptr->pl_head_ptr, \ + cache_ptr->pl_tail_ptr, cache_ptr->pl_len, \ + cache_ptr->pl_size) + + + /* modified LRU specific code */ + + /* insert the entry at the head of the LRU list. */ + + H5C__DLL_PREPEND(entry_ptr, cache_ptr->LRU_head_ptr, \ + cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ + cache_ptr->LRU_list_size) + + /* Similarly, insert the entry at the head of either the clean or + * dirty LRU list as appropriate. + */ + + if ( entry_ptr->dirty ) { + + H5C__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) + + } else { + + H5C__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) + } + + /* End modified LRU specific code. */ + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_update_rp_for_unprotect() */ + + +/*************************************************************************/ +/*************************************************************************/ +/*************************************************************************/ +/********************************** END **********************************/ +/*************************************************************************/ +/*************************************************************************/ +/*************************************************************************/ + diff --git a/src/H5Cprivate.h b/src/H5Cprivate.h new file mode 100644 index 0000000..2a0d3db --- /dev/null +++ b/src/H5Cprivate.h @@ -0,0 +1,374 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*------------------------------------------------------------------------- + * + * Created: H5Cprivate.h + * 6/3/04 + * John Mainzer + * + * Purpose: Constants and typedefs available to the rest of the + * library. + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +#ifndef _H5Cprivate_H +#define _H5Cprivate_H + +#include "H5Cpublic.h" /*public prototypes */ + +/* Pivate headers needed by this header */ +#include "H5private.h" /* Generic Functions */ +#include "H5Fprivate.h" /* File access */ + +#define H5C_DO_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 H5C_MAX_ENTRY_SIZE ((size_t)(100 * 1024)) + +/* H5C_COLLECT_CACHE_STATS controls overall collection of statistics + * on cache activity. In general, this #define should be set to 0. + */ +#define H5C_COLLECT_CACHE_STATS 0 + +/* H5C_COLLECT_CACHE_ENTRY_STATS controls collection of statistics + * in individual cache entries. + * + * H5C_COLLECT_CACHE_ENTRY_STATS should only be defined to true if + * H5C_COLLECT_CACHE_STATS is also defined to true. + */ +#if H5C_COLLECT_CACHE_STATS + +#define H5C_COLLECT_CACHE_ENTRY_STATS 1 + +#else + +#define H5C_COLLECT_CACHE_ENTRY_STATS 0 + +#endif /* H5C_COLLECT_CACHE_STATS */ + +/* + * 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 H5C_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. + */ + +typedef void *(*H5C_load_func_t)(H5F_t *f, + hid_t dxpl_id, + haddr_t addr, + const void *udata1, + void *udata2); +typedef herr_t (*H5C_flush_func_t)(H5F_t *f, + hid_t dxpl_id, + hbool_t dest, + haddr_t addr, + void *thing); +typedef herr_t (*H5C_dest_func_t)(H5F_t *f, + void *thing); +typedef herr_t (*H5C_clear_func_t)(H5F_t *f, + void *thing, + hbool_t dest); +typedef herr_t (*H5C_size_func_t)(H5F_t *f, + void *thing, + size_t *size_ptr); + +typedef struct H5C_class_t { + int id; + H5C_load_func_t load; + H5C_flush_func_t flush; + H5C_dest_func_t dest; + H5C_clear_func_t clear; + H5C_size_func_t size; +} H5C_class_t; + + +/* Type defintions of call back functions used by the cache as a whole */ + +typedef herr_t (*H5C_write_permitted_func_t)(H5F_t *f, + hid_t dxpl_id, + hbool_t * write_permitted_ptr); + + +/* Default max cache size and min clean size are give here to make + * them generally accessable. + */ + +#define H5C__DEFAULT_MAX_CACHE_SIZE ((size_t)(2 * 1024 * 1024)) +#define H5C__DEFAULT_MIN_CLEAN_SIZE ((size_t)(1 * 1024 * 1024)) + + +/**************************************************************************** + * + * structure H5C_cache_entry_t + * + * Instances of the H5C_cache_entry_t structure are used to store meta data + * cache entries in an a threaded binary B-tree. See H5TB.c for the + * particulars of the B-tree. + * + * 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 dirty field. All other fields are + * managed by the cache. + * + * Note that our current implementation of a threaded binary B-tree will + * occasionaly change the node a particular datum is associated with. Thus + * this structure does not have a back pointer to its B-tree node. If we + * ever modify the threaded binary B-tree code to fix this, a back pointer + * would save us a few tree traversals. + * + * The fields of this structure are discussed individually below: + * + * JRM - 4/26/04 + * + * 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. + * + * type: Pointer to the instance of H5C_class_t containing pointers + * to the methods for cache entries of the current type. This + * field should be NULL when the instance of H5C_cache_entry_t + * is not in use. + * + * The name is not particularly descriptive, but is retained + * to avoid changes in existing code. + * + * 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. + * + * 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. + * + * + * 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 H5C_t in H5C.c 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 H5C_COLLECT_CACHE_STATS + * and H5C_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. + * + ****************************************************************************/ + +typedef struct H5C_cache_entry_t +{ + haddr_t addr; + size_t size; + const H5C_class_t * type; + hbool_t dirty; + hbool_t protected; + + /* fields supporting replacement policies: */ + + struct H5C_cache_entry_t * next; + struct H5C_cache_entry_t * prev; + struct H5C_cache_entry_t * aux_next; + struct H5C_cache_entry_t * aux_prev; + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + /* cache entry stats fields */ + + int32_t accesses; + int32_t clears; + int32_t flushes; + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + +} H5C_cache_entry_t; + + +/* Typedef for the main structure for the cache (defined in H5C.c) */ + +typedef struct H5C_t H5C_t; + +/* + * Library prototypes. + */ +H5_DLL H5C_t * H5C_create(size_t max_cache_size, + size_t min_clean_size, + int max_type_id, + const char * (* type_name_table_ptr)[], + H5C_write_permitted_func_t check_write_permitted); + +H5_DLL herr_t H5C_dest(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr); + +H5_DLL herr_t H5C_dest_empty(H5C_t * cache_ptr); + +H5_DLL herr_t H5C_flush_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + unsigned flags); + +H5_DLL herr_t H5C_insert_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + void * thing); + +H5_DLL herr_t H5C_rename_entry(H5F_t * f, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t old_addr, + haddr_t new_addr); + +H5_DLL void * H5C_protect(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2); + +H5_DLL herr_t H5C_unprotect(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + void * thing, + hbool_t deleted); + +H5_DLL herr_t H5C_stats(H5C_t * cache_ptr, + const char * cache_name, + hbool_t display_detailed_stats); + +void H5C_stats__reset(H5C_t * cache_ptr); + +H5_DLL herr_t H5C_set_skip_flags(H5C_t * cache_ptr, + hbool_t skip_file_checks, + hbool_t skip_dxpl_id_checks); + +#endif /* !_H5Cprivate_H */ + diff --git a/src/H5Cpublic.h b/src/H5Cpublic.h new file mode 100644 index 0000000..d4e82be --- /dev/null +++ b/src/H5Cpublic.h @@ -0,0 +1,40 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*------------------------------------------------------------------------- + * + * Created: H5Cproto.h + * June 4, 2005 + * John Mainzer + * + * Purpose: Public include file for cache functions. + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +#ifndef _H5Cpublic_H +#define _H5Cpublic_H + +/* Public headers needed by this file */ +#include "H5public.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif +#endif @@ -120,6 +120,35 @@ static herr_t H5E_walk_cb(unsigned n, const H5E_error_t *err_desc, void *client static herr_t H5E_get_auto(const H5E_t *estack, H5E_auto_t *func, void **client_data); static herr_t H5E_set_auto(H5E_t *estack, H5E_auto_t func, void *client_data); + +/*------------------------------------------------------------------------- + * Function: H5E_init + * + * Purpose: Initialize the interface from some other layer. + * + * Return: Success: non-negative + * + * Failure: negative + * + * Programmer: Quincey Koziol + * Tuesday, June 29, 2004 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +herr_t +H5E_init(void) +{ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5E_init, FAIL) + /* FUNC_ENTER() does all the work */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} + /*-------------------------------------------------------------------------- * Function: H5E_init_interface * diff --git a/src/H5Edefin.h b/src/H5Edefin.h index 2d70e0d..dfd9714 100644 --- a/src/H5Edefin.h +++ b/src/H5Edefin.h @@ -90,8 +90,7 @@ hid_t H5E_NOFILTER_g = FAIL; /* Requested filter is not available */ hid_t H5E_CALLBACK_g = FAIL; /* Callback failed */ hid_t H5E_CANAPPLY_g = FAIL; /* Error from filter 'can apply' callback */ hid_t H5E_SETLOCAL_g = FAIL; /* Error from filter 'set local' callback */ -hid_t H5E_NOENCODER_g = FAIL; /* Filter present but encoder not enabled */ -hid_t H5E_NODECODER_g = FAIL; /* Filter present but decoder not enabled */ +hid_t H5E_NOENCODER_g = FAIL; /* Filter present but encoding disabled */ /* Datatype conversion errors */ hid_t H5E_CANTCONVERT_g = FAIL; /* Can't convert datatypes */ @@ -116,6 +115,7 @@ hid_t H5E_ALREADYEXISTS_g = FAIL; /* Object already exists */ hid_t H5E_CANTLOCK_g = FAIL; /* Unable to lock object */ hid_t H5E_CANTUNLOCK_g = FAIL; /* Unable to unlock object */ hid_t H5E_CANTGC_g = FAIL; /* Unable to garbage collect */ +hid_t H5E_CANTGETSIZE_g = FAIL; /* Unable to compute size */ /* Generic low-level file I/O errors */ hid_t H5E_SEEKERROR_g = FAIL; /* Seek failed */ @@ -151,6 +151,11 @@ hid_t H5E_CANTSERIALIZE_g = FAIL; /* Unable to serialize data from cache * hid_t H5E_CANTLOAD_g = FAIL; /* Unable to load metadata into cache */ hid_t H5E_PROTECT_g = FAIL; /* Protected metadata error */ hid_t H5E_NOTCACHED_g = FAIL; /* Metadata not currently cached */ +hid_t H5E_SYSTEM_g = FAIL; /* Internal error detected */ +hid_t H5E_CANTINS_g = FAIL; /* Unable to insert metadata into cache */ +hid_t H5E_CANTRENAME_g = FAIL; /* Unable to rename metadata */ +hid_t H5E_CANTPROTECT_g = FAIL; /* Unable to protect metadata */ +hid_t H5E_CANTUNPROTECT_g = FAIL; /* Unable to unprotect metadata */ /* Group related errors */ hid_t H5E_CANTOPENOBJ_g = FAIL; /* Can't open object */ diff --git a/src/H5Einit.h b/src/H5Einit.h index 92b7158..400f82c 100644 --- a/src/H5Einit.h +++ b/src/H5Einit.h @@ -321,15 +321,10 @@ if((msg = H5E_create_msg(cls, H5E_MINOR, "Error from filter 'set local' callback if((H5E_SETLOCAL_g = H5I_register(H5I_ERROR_MSG, msg))<0) HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") assert(H5E_NOENCODER_g==(-1)); -if((msg = H5E_create_msg(cls, H5E_MINOR, "Error from filter 'no encoder' callback"))==NULL) +if((msg = H5E_create_msg(cls, H5E_MINOR, "Filter present but encoding disabled"))==NULL) HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") if((H5E_NOENCODER_g = H5I_register(H5I_ERROR_MSG, msg))<0) HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") -assert(H5E_NODECODER_g==(-1)); -if((msg = H5E_create_msg(cls, H5E_MINOR, "Error from filter 'no decoder' callback"))==NULL) - HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") -if((H5E_NODECODER_g = H5I_register(H5I_ERROR_MSG, msg))<0) - HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") /* Datatype conversion errors */ assert(H5E_CANTCONVERT_g==(-1)); @@ -418,6 +413,11 @@ if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to garbage collect"))==NULL) HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") if((H5E_CANTGC_g = H5I_register(H5I_ERROR_MSG, msg))<0) HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") +assert(H5E_CANTGETSIZE_g==(-1)); +if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to compute size"))==NULL) + HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") +if((H5E_CANTGETSIZE_g = H5I_register(H5I_ERROR_MSG, msg))<0) + HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") /* Generic low-level file I/O errors */ assert(H5E_SEEKERROR_g==(-1)); @@ -561,6 +561,31 @@ if((msg = H5E_create_msg(cls, H5E_MINOR, "Metadata not currently cached"))==NULL HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") if((H5E_NOTCACHED_g = H5I_register(H5I_ERROR_MSG, msg))<0) HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") +assert(H5E_SYSTEM_g==(-1)); +if((msg = H5E_create_msg(cls, H5E_MINOR, "Internal error detected"))==NULL) + HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") +if((H5E_SYSTEM_g = H5I_register(H5I_ERROR_MSG, msg))<0) + HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") +assert(H5E_CANTINS_g==(-1)); +if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to insert metadata into cache"))==NULL) + HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") +if((H5E_CANTINS_g = H5I_register(H5I_ERROR_MSG, msg))<0) + HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") +assert(H5E_CANTRENAME_g==(-1)); +if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to rename metadata"))==NULL) + HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") +if((H5E_CANTRENAME_g = H5I_register(H5I_ERROR_MSG, msg))<0) + HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") +assert(H5E_CANTPROTECT_g==(-1)); +if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to protect metadata"))==NULL) + HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") +if((H5E_CANTPROTECT_g = H5I_register(H5I_ERROR_MSG, msg))<0) + HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") +assert(H5E_CANTUNPROTECT_g==(-1)); +if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to unprotect metadata"))==NULL) + HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") +if((H5E_CANTUNPROTECT_g = H5I_register(H5I_ERROR_MSG, msg))<0) + HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") /* Group related errors */ assert(H5E_CANTOPENOBJ_g==(-1)); diff --git a/src/H5Eprivate.h b/src/H5Eprivate.h index 6f858f9..b874157 100644 --- a/src/H5Eprivate.h +++ b/src/H5Eprivate.h @@ -108,6 +108,7 @@ typedef struct H5E_print_t { #define HGOTO_DONE(ret_val) {ret_value = ret_val; goto done;} /* Library-private functions defined in H5E package */ +H5_DLL herr_t H5E_init(void); H5_DLL herr_t H5E_push(H5E_t *estack, const char *file, const char *func, unsigned line, hid_t cls_id, hid_t maj_id, hid_t min_id, const char *desc); H5_DLL herr_t H5E_clear(H5E_t *estack); diff --git a/src/H5Epubgen.h b/src/H5Epubgen.h index 958e91c..a0938ae 100644 --- a/src/H5Epubgen.h +++ b/src/H5Epubgen.h @@ -147,14 +147,12 @@ H5_DLLVAR hid_t H5E_CANTALLOC_g; /* Can't allocate from file */ #define H5E_CALLBACK (H5OPEN H5E_CALLBACK_g) #define H5E_CANAPPLY (H5OPEN H5E_CANAPPLY_g) #define H5E_SETLOCAL (H5OPEN H5E_SETLOCAL_g) -#define H5E_NOENCODER (H5OPEN H5E_NOENCODER_g) -#define H5E_NODECODER (H5OPEN H5E_NODECODER_g) +#define H5E_NOENCODER (H5OPEN H5E_NOENCODER_g) H5_DLLVAR hid_t H5E_NOFILTER_g; /* Requested filter is not available */ H5_DLLVAR hid_t H5E_CALLBACK_g; /* Callback failed */ H5_DLLVAR hid_t H5E_CANAPPLY_g; /* Error from filter 'can apply' callback */ H5_DLLVAR hid_t H5E_SETLOCAL_g; /* Error from filter 'set local' callback */ -H5_DLLVAR hid_t H5E_NOENCODER_g; /* Filter present, but encoding disabled */ -H5_DLLVAR hid_t H5E_NODECODER_g; /* Filter present, but decoding disabled */ +H5_DLLVAR hid_t H5E_NOENCODER_g; /* Filter present but encoding disabled */ /* Datatype conversion errors */ #define H5E_CANTCONVERT (H5OPEN H5E_CANTCONVERT_g) @@ -188,6 +186,7 @@ H5_DLLVAR hid_t H5E_CANTDELETE_g; /* Can't delete message */ #define H5E_CANTLOCK (H5OPEN H5E_CANTLOCK_g) #define H5E_CANTUNLOCK (H5OPEN H5E_CANTUNLOCK_g) #define H5E_CANTGC (H5OPEN H5E_CANTGC_g) +#define H5E_CANTGETSIZE (H5OPEN H5E_CANTGETSIZE_g) H5_DLLVAR hid_t H5E_NOSPACE_g; /* No space available for allocation */ H5_DLLVAR hid_t H5E_CANTCOPY_g; /* Unable to copy object */ H5_DLLVAR hid_t H5E_CANTFREE_g; /* Unable to free object */ @@ -195,6 +194,7 @@ H5_DLLVAR hid_t H5E_ALREADYEXISTS_g;/* Object already exists */ H5_DLLVAR hid_t H5E_CANTLOCK_g; /* Unable to lock object */ H5_DLLVAR hid_t H5E_CANTUNLOCK_g; /* Unable to unlock object */ H5_DLLVAR hid_t H5E_CANTGC_g; /* Unable to garbage collect */ +H5_DLLVAR hid_t H5E_CANTGETSIZE_g; /* Unable to compute size */ /* Generic low-level file I/O errors */ #define H5E_SEEKERROR (H5OPEN H5E_SEEKERROR_g) @@ -252,11 +252,21 @@ H5_DLLVAR hid_t H5E_MOUNT_g; /* File mount error */ #define H5E_CANTLOAD (H5OPEN H5E_CANTLOAD_g) #define H5E_PROTECT (H5OPEN H5E_PROTECT_g) #define H5E_NOTCACHED (H5OPEN H5E_NOTCACHED_g) +#define H5E_SYSTEM (H5OPEN H5E_SYSTEM_g) +#define H5E_CANTINS (H5OPEN H5E_CANTINS_g) +#define H5E_CANTRENAME (H5OPEN H5E_CANTRENAME_g) +#define H5E_CANTPROTECT (H5OPEN H5E_CANTPROTECT_g) +#define H5E_CANTUNPROTECT (H5OPEN H5E_CANTUNPROTECT_g) H5_DLLVAR hid_t H5E_CANTFLUSH_g; /* Unable to flush data from cache */ H5_DLLVAR hid_t H5E_CANTSERIALIZE_g;/* Unable to serialize data from cache */ H5_DLLVAR hid_t H5E_CANTLOAD_g; /* Unable to load metadata into cache */ H5_DLLVAR hid_t H5E_PROTECT_g; /* Protected metadata error */ H5_DLLVAR hid_t H5E_NOTCACHED_g; /* Metadata not currently cached */ +H5_DLLVAR hid_t H5E_SYSTEM_g; /* Internal error detected */ +H5_DLLVAR hid_t H5E_CANTINS_g; /* Unable to insert metadata into cache */ +H5_DLLVAR hid_t H5E_CANTRENAME_g; /* Unable to rename metadata */ +H5_DLLVAR hid_t H5E_CANTPROTECT_g; /* Unable to protect metadata */ +H5_DLLVAR hid_t H5E_CANTUNPROTECT_g;/* Unable to unprotect metadata */ /* Group related errors */ #define H5E_CANTOPENOBJ (H5OPEN H5E_CANTOPENOBJ_g) diff --git a/src/H5Eterm.h b/src/H5Eterm.h index 2acd75d..6be8cb6 100644 --- a/src/H5Eterm.h +++ b/src/H5Eterm.h @@ -91,9 +91,8 @@ H5E_CANTALLOC_g= H5E_NOFILTER_g= H5E_CALLBACK_g= H5E_CANAPPLY_g= -H5E_SETLOCAL_g= +H5E_SETLOCAL_g= H5E_NOENCODER_g= -H5E_NODECODER_g= /* Datatype conversion errors */ H5E_CANTCONVERT_g= @@ -117,7 +116,8 @@ H5E_CANTFREE_g= H5E_ALREADYEXISTS_g= H5E_CANTLOCK_g= H5E_CANTUNLOCK_g= -H5E_CANTGC_g= +H5E_CANTGC_g= +H5E_CANTGETSIZE_g= /* Generic low-level file I/O errors */ H5E_SEEKERROR_g= @@ -152,7 +152,12 @@ H5E_CANTFLUSH_g= H5E_CANTSERIALIZE_g= H5E_CANTLOAD_g= H5E_PROTECT_g= -H5E_NOTCACHED_g= +H5E_NOTCACHED_g= +H5E_SYSTEM_g= +H5E_CANTINS_g= +H5E_CANTRENAME_g= +H5E_CANTPROTECT_g= +H5E_CANTUNPROTECT_g= /* Group related errors */ H5E_CANTOPENOBJ_g= @@ -1838,6 +1838,7 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id, hid_t d * We've just opened a fresh new file (or truncated one). We need * to create & write the superblock. */ + #ifdef H5_HAVE_FPHDF5 if (!H5FD_is_fphdf5_driver(lf) || H5FD_fphdf5_is_captain(lf)) { #endif /* H5_HAVE_FPHDF5 */ @@ -1979,7 +1980,7 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id, hid_t d if(fc_degree!=H5F_CLOSE_DEFAULT && fc_degree != shared->fc_degree) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "file close degree doesn't match") } - + /* Success */ ret_value = file; @@ -1987,6 +1988,7 @@ done: if (!ret_value && file) if(H5F_dest(file, dxpl_id)<0) HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, NULL, "problems closing file") + FUNC_LEAVE_NOAPI(ret_value) } @@ -3272,9 +3274,9 @@ H5F_close(H5F_t *f) /* Only flush at this point if the file will be closed */ assert(closing); /* Dump debugging info */ -#ifdef H5AC_DEBUG +#if H5C_COLLECT_CACHE_STATS H5AC_stats(f); -#endif /* H5AC_DEBUG */ +#endif /* H5AC_COLLECT_CACHE_STATS */ /* Only try to flush the file if it was opened with write access */ if(f->intent&H5F_ACC_RDWR) { diff --git a/src/H5Gnode.c b/src/H5Gnode.c index 7fc436c..530020e 100644 --- a/src/H5Gnode.c +++ b/src/H5Gnode.c @@ -72,6 +72,7 @@ static herr_t H5G_node_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t a H5G_node_t *sym); static herr_t H5G_node_dest(H5F_t *f, H5G_node_t *sym); static herr_t H5G_node_clear(H5F_t *f, H5G_node_t *sym, hbool_t destroy); +static herr_t H5G_compute_size(H5F_t *f, H5G_node_t *sym, size_t *size_ptr); /* B-tree callbacks */ static size_t H5G_node_sizeof_rkey(H5F_t *f, const void *_udata); @@ -107,6 +108,7 @@ const H5AC_class_t H5AC_SNODE[1] = {{ (H5AC_flush_func_t)H5G_node_flush, (H5AC_dest_func_t)H5G_node_dest, (H5AC_clear_func_t)H5G_node_clear, + (H5AC_size_func_t)H5G_compute_size, }}; /* H5G inherits B-tree like properties from H5B */ @@ -619,6 +621,43 @@ done: /*------------------------------------------------------------------------- + * Function: H5G_compute_size + * + * Purpose: Compute the size in bytes of the specified instance of + * H5G_node_t on disk, and return it in *size_ptr. On failure + * the value of size_ptr is undefined. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/13/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +H5G_compute_size(H5F_t *f, H5G_node_t UNUSED *sym, size_t *size_ptr) +{ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5G_compute_size, FAIL); + + /* + * Check arguments. + */ + assert(f); + assert(size_ptr); + + *size_ptr = H5G_node_size(f); + +done: + FUNC_LEAVE_NOAPI(ret_value); + +} /* H5G_compute_size() */ + + +/*------------------------------------------------------------------------- * Function: H5G_node_create * * Purpose: Creates a new empty symbol table node. This function is @@ -120,7 +120,7 @@ /* Private typedefs */ /* PRIVATE PROTOTYPES */ -static H5HG_heap_t *H5HG_create(H5F_t *f, hid_t dxpl_id, size_t size); +static haddr_t H5HG_create(H5F_t *f, hid_t dxpl_id, size_t size); #ifdef NOT_YET static void *H5HG_peek(H5F_t *f, hid_t dxpl_id, H5HG_t *hobj); #endif /* NOT_YET */ @@ -132,6 +132,7 @@ static herr_t H5HG_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, H5HG_heap_t *heap); static herr_t H5HG_dest(H5F_t *f, H5HG_heap_t *heap); static herr_t H5HG_clear(H5F_t *f, H5HG_heap_t *heap, hbool_t destroy); +static herr_t H5HG_compute_size(H5F_t *f, H5HG_heap_t *heap, size_t *size_ptr); /* * H5HG inherits cache-like properties from H5AC @@ -142,6 +143,7 @@ const H5AC_class_t H5AC_GHEAP[1] = {{ (H5AC_flush_func_t)H5HG_flush, (H5AC_dest_func_t)H5HG_dest, (H5AC_clear_func_t)H5HG_clear, + (H5AC_size_func_t)H5HG_compute_size, }}; /* Declare a free list to manage the H5HG_t struct */ @@ -173,18 +175,27 @@ H5FL_BLK_DEFINE_STATIC(heap_chunk); * * Modifications: * + * John Mainzer 5/26/04 + * Modified function to return the disk address of the new + * global heap collection, or HADDR_UNDEF on failure. This + * is necessary, as in some cases (i.e. flexible parallel) + * H5AC_set() will imediately flush and destroy the in memory + * version of the new collection. For the same reason, I + * moved the code which places the new collection on the cwfs + * list to just before the call to H5AC_set(). + * *------------------------------------------------------------------------- */ -static H5HG_heap_t * +static haddr_t H5HG_create (H5F_t *f, hid_t dxpl_id, size_t size) { H5HG_heap_t *heap = NULL; - H5HG_heap_t *ret_value = NULL; + haddr_t ret_value = HADDR_UNDEF; uint8_t *p = NULL; haddr_t addr; size_t n; - FUNC_ENTER_NOAPI(H5HG_create, NULL); + FUNC_ENTER_NOAPI(H5HG_create, HADDR_UNDEF); /* Check args */ assert (f); @@ -194,19 +205,24 @@ H5HG_create (H5F_t *f, hid_t dxpl_id, size_t size) /* Create it */ H5_CHECK_OVERFLOW(size,size_t,hsize_t); - if (HADDR_UNDEF==(addr=H5MF_alloc(f, H5FD_MEM_GHEAP, dxpl_id, (hsize_t)size))) - HGOTO_ERROR (H5E_HEAP, H5E_CANTINIT, NULL, "unable to allocate file space for global heap"); + if ( HADDR_UNDEF== + (addr=H5MF_alloc(f, H5FD_MEM_GHEAP, dxpl_id, (hsize_t)size))) + HGOTO_ERROR (H5E_HEAP, H5E_CANTINIT, HADDR_UNDEF, \ + "unable to allocate file space for global heap"); if (NULL==(heap = H5FL_MALLOC (H5HG_heap_t))) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); + HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, HADDR_UNDEF, \ + "memory allocation failed"); heap->addr = addr; heap->size = size; heap->cache_info.dirty = TRUE; if (NULL==(heap->chunk = H5FL_BLK_MALLOC (heap_chunk,size))) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); + HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, HADDR_UNDEF, \ + "memory allocation failed"); heap->nalloc = H5HG_NOBJS (f, size); heap->next_idx = 1; /* skip index 0, which is used for the free object */ if (NULL==(heap->obj = H5FL_SEQ_MALLOC (H5HG_obj_t,heap->nalloc))) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); + HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, HADDR_UNDEF, \ + "memory allocation failed"); /* Initialize the header */ HDmemcpy (heap->chunk, H5HG_MAGIC, H5HG_SIZEOF_MAGIC); @@ -243,32 +259,42 @@ H5HG_create (H5F_t *f, hid_t dxpl_id, size_t size) HDmemset (p, 0, (size_t)((heap->chunk+heap->size) - p)); #endif /* OLD_WAY */ - /* Add the heap to the cache */ - if (H5AC_set (f, dxpl_id, H5AC_GHEAP, addr, heap)<0) - HGOTO_ERROR (H5E_HEAP, H5E_CANTINIT, NULL, "unable to cache global heap collection"); - /* Add this heap to the beginning of the CWFS list */ if (NULL==f->shared->cwfs) { f->shared->cwfs = H5MM_malloc (H5HG_NCWFS * sizeof(H5HG_heap_t*)); if (NULL==(f->shared->cwfs)) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); + HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, HADDR_UNDEF, \ + "memory allocation failed"); f->shared->cwfs[0] = heap; f->shared->ncwfs = 1; } else { - HDmemmove (f->shared->cwfs+1, f->shared->cwfs, MIN (f->shared->ncwfs, H5HG_NCWFS-1)*sizeof(H5HG_heap_t*)); + HDmemmove (f->shared->cwfs+1, f->shared->cwfs, + MIN (f->shared->ncwfs, H5HG_NCWFS-1)*sizeof(H5HG_heap_t*)); f->shared->cwfs[0] = heap; f->shared->ncwfs = MIN (H5HG_NCWFS, f->shared->ncwfs+1); } - ret_value = heap; + /* Add the heap to the cache */ + if (H5AC_set (f, dxpl_id, H5AC_GHEAP, addr, heap)<0) + HGOTO_ERROR (H5E_HEAP, H5E_CANTINIT, HADDR_UNDEF, \ + "unable to cache global heap collection"); + + ret_value = addr; done: - if (!ret_value && heap) { - if(H5HG_dest(f,heap)<0) - HDONE_ERROR(H5E_HEAP, H5E_CANTFREE, NULL, "unable to destroy global heap collection"); + + if ( ( ! ( H5F_addr_defined(addr) ) ) && ( heap ) ) { + + if ( H5HG_dest(f,heap) < 0 ) { + + HDONE_ERROR(H5E_HEAP, H5E_CANTFREE, HADDR_UNDEF, \ + "unable to destroy global heap collection"); + } } + FUNC_LEAVE_NOAPI(ret_value); -} + +} /* H5HG_create() */ /*------------------------------------------------------------------------- @@ -588,6 +614,41 @@ done: /*------------------------------------------------------------------------- + * Function: H5HG_compute_size + * + * Purpose: Compute the size in bytes of the specified instance of + * H5HG_heap_t on disk, and return it in *len_ptr. On failure, + * the value of *len_ptr is undefined. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/13/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +H5HG_compute_size(H5F_t UNUSED *f, H5HG_heap_t *heap, size_t *size_ptr) +{ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5HG_compute_size, FAIL); + + /* Check arguments */ + HDassert(heap); + HDassert(size_ptr); + + *size_ptr = heap->size; + +done: + FUNC_LEAVE_NOAPI(ret_value); + +} /* H5HG_compute_size() */ + + +/*------------------------------------------------------------------------- * Function: H5HG_alloc * * Purpose: Given a heap with enough free space, this function will split @@ -816,6 +877,23 @@ done: * * Modifications: * + * John Mainzer -- 5/24/04 + * The function used to modify the heap without protecting + * the relevant collection first. I did a half assed job + * of fixing the problem, which should hold until we try to + * support multi-threading. At that point it will have to + * be done right. + * + * See in line comment of this date for more details. + * + * John Mainzer - 5/26/04 + * Modified H5HG_create() to return the disk address of the + * new collection, instead of the address of its + * representation in core. This was necessary as in FP + * mode, the cache will immediately flush and destroy any + * entry inserted in it via H5AC_set(). I then modified + * this function to account for the change in H5HG_create(). + * *------------------------------------------------------------------------- */ herr_t @@ -824,6 +902,7 @@ H5HG_insert (H5F_t *f, hid_t dxpl_id, size_t size, void *obj, H5HG_t *hobj/*out* size_t need; /*total space needed for object */ int cwfsno; unsigned idx; + haddr_t addr = HADDR_UNDEF; H5HG_heap_t *heap = NULL; hbool_t found=0; /* Flag to indicate a heap with enough space was found */ herr_t ret_value=SUCCEED; /* Return value */ @@ -840,8 +919,36 @@ H5HG_insert (H5F_t *f, hid_t dxpl_id, size_t size, void *obj, H5HG_t *hobj/*out* /* Find a large enough collection on the CWFS list */ need = H5HG_SIZEOF_OBJHDR(f) + H5HG_ALIGN(size); + + /* Note that we don't have metadata cache locks on the entries in + * f->shared->cwfs. + * + * In the current situation, this doesn't matter, as we are single + * threaded, and as best I can tell, entries are added to and deleted + * from f->shared->cwfs as they are added to and deleted from the + * metadata cache. + * + * To be proper, we should either lock each entry in f->shared->cwfs + * as we examine it, or lock the whole array. However, at present + * I don't see the point as there will be significant overhead, + * and protecting and unprotecting all the collections in the global + * heap on a regular basis will skew the replacement policy. + * + * However, there is a bigger issue -- as best I can tell, we only look + * for free space in global heap chunks that are in cache. If we can't + * find any, we allocate a new chunk. This may be a problem in FP mode, + * as the metadata cache is disabled. Do we allocate a new heap + * collection for every entry in this case? + * + * Note that all this comes from a cursory read of the source. Don't + * take any of it as gospel. + * JRM - 5/24/04 + */ + for (cwfsno=0; cwfsno<f->shared->ncwfs; cwfsno++) { if (f->shared->cwfs[cwfsno]->obj[0].size>=need) { + + addr = f->shared->cwfs[cwfsno]->addr; found=1; break; } /* end if */ @@ -856,6 +963,7 @@ H5HG_insert (H5F_t *f, hid_t dxpl_id, size_t size, void *obj, H5HG_t *hobj/*out* if((f->shared->cwfs[cwfsno]->size+need)<=H5HG_MAXSIZE && H5MF_can_extend(f,H5FD_MEM_GHEAP,f->shared->cwfs[cwfsno]->addr,(hsize_t)f->shared->cwfs[cwfsno]->size,(hsize_t)need)) { if(H5HG_extend(f,f->shared->cwfs[cwfsno],size)<0) HGOTO_ERROR (H5E_HEAP, H5E_CANTINIT, FAIL, "unable to extend global heap collection"); + addr = f->shared->cwfs[cwfsno]->addr; found=1; break; } /* end if */ @@ -866,19 +974,23 @@ H5HG_insert (H5F_t *f, hid_t dxpl_id, size_t size, void *obj, H5HG_t *hobj/*out* * If we didn't find any collection with enough free space then allocate a * new collection large enough for the message plus the collection header. */ + if (!found) { - if (NULL==(heap=H5HG_create (f, dxpl_id, need+H5HG_SIZEOF_HDR (f)))) - HGOTO_ERROR (H5E_HEAP, H5E_CANTINIT, FAIL, "unable to allocate a global heap collection"); - assert (f->shared->ncwfs>0); - assert (f->shared->cwfs[0]==heap); - assert (f->shared->cwfs[0]->obj[0].size >= need); + + addr = H5HG_create(f, dxpl_id, need+H5HG_SIZEOF_HDR (f)); + + if ( ! ( H5F_addr_defined(addr) ) ) { + + HGOTO_ERROR (H5E_HEAP, H5E_CANTINIT, FAIL, \ + "unable to allocate a global heap collection"); + } cwfsno = 0; } /* end if */ else { - /* Found a heap with enough space */ - heap = f->shared->cwfs[cwfsno]; - /* Move the collection forward in the CWFS list, if it's not already at the front */ + /* Move the collection forward in the CWFS list, if it's not + * already at the front + */ if (cwfsno>0) { H5HG_heap_t *tmp = f->shared->cwfs[cwfsno]; f->shared->cwfs[cwfsno] = f->shared->cwfs[cwfsno-1]; @@ -886,7 +998,15 @@ H5HG_insert (H5F_t *f, hid_t dxpl_id, size_t size, void *obj, H5HG_t *hobj/*out* --cwfsno; } /* end if */ } /* end else */ + + HDassert(H5F_addr_defined(addr)); + if ( NULL == (heap = H5AC_protect(f, dxpl_id, H5AC_GHEAP, + addr, NULL, NULL, H5AC_WRITE)) ) { + + HGOTO_ERROR (H5E_HEAP, H5E_CANTLOAD, FAIL, "unable to load heap"); + } + /* Split the free space to make room for the new object */ idx = H5HG_alloc (f, heap, size); assert (idx>0); @@ -907,8 +1027,17 @@ H5HG_insert (H5F_t *f, hid_t dxpl_id, size_t size, void *obj, H5HG_t *hobj/*out* hobj->idx = idx; done: + + if ( heap && + H5AC_unprotect(f, dxpl_id, H5AC_GHEAP, heap->addr, heap, FALSE) + != SUCCEED ) { + + HDONE_ERROR(H5E_HEAP, H5E_PROTECT, FAIL, "unable to unprotect heap."); + } + FUNC_LEAVE_NOAPI(ret_value); -} + +} /* H5HG_insert() */ #ifdef NOT_YET @@ -72,6 +72,7 @@ static H5HL_t *H5HL_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, const void *udat static herr_t H5HL_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, H5HL_t *heap); static herr_t H5HL_dest(H5F_t *f, H5HL_t *heap); static herr_t H5HL_clear(H5F_t *f, H5HL_t *heap, hbool_t destroy); +static herr_t H5HL_compute_size(H5F_t *f, H5HL_t *heap, size_t *size_ptr); /* * H5HL inherits cache-like properties from H5AC @@ -82,6 +83,7 @@ const H5AC_class_t H5AC_LHEAP[1] = {{ (H5AC_flush_func_t)H5HL_flush, (H5AC_dest_func_t)H5HL_dest, (H5AC_clear_func_t)H5HL_clear, + (H5AC_size_func_t)H5HL_compute_size, }}; /* Declare a free list to manage the H5HL_free_t struct */ @@ -669,6 +671,42 @@ done: /*------------------------------------------------------------------------- + * Function: H5HL_compute_size + * + * Purpose: Compute the size in bytes of the specified instance of + * H5HL_t on disk, and return it in *len_ptr. On failure, + * the value of *len_ptr is undefined. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/13/04 + * + * Modifications: + *------------------------------------------------------------------------- + */ +static herr_t +H5HL_compute_size(H5F_t *f, H5HL_t *heap, size_t *size_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5HL_compute_size, FAIL); + + /* check arguments */ + HDassert(f); + HDassert(heap); + HDassert(size_ptr); + + *size_ptr = H5HL_SIZEOF_HDR(f) + heap->disk_alloc; + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5HL_compute_size() */ + + +/*------------------------------------------------------------------------- * Function: H5HL_read * * Purpose: Reads some object (or part of an object) from the heap @@ -91,6 +91,7 @@ static H5O_t *H5O_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, const void *_udata static herr_t H5O_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5O_t *oh); static herr_t H5O_dest(H5F_t *f, H5O_t *oh); static herr_t H5O_clear(H5F_t *f, H5O_t *oh, hbool_t destroy); +static herr_t H5O_compute_size(H5F_t *f, H5O_t *oh, size_t *size_ptr); /* H5O inherits cache-like properties from H5AC */ static const H5AC_class_t H5AC_OHDR[1] = {{ @@ -99,6 +100,7 @@ static const H5AC_class_t H5AC_OHDR[1] = {{ (H5AC_flush_func_t)H5O_flush, (H5AC_dest_func_t)H5O_dest, (H5AC_clear_func_t)H5O_clear, + (H5AC_size_func_t)H5O_compute_size, }}; /* ID to type mapping */ @@ -893,6 +895,60 @@ done: /*------------------------------------------------------------------------- + * Function: H5O_compute_size + * + * Purpose: Compute the size in bytes of the specified instance of + * H5O_t on disk, and return it in *len_ptr. On failure, + * the value of *len_ptr is undefined. + * + * The value returned will probably be low unless the object + * has just been flushed, as we simply total up the size of + * the header with the sizes of the chunks. Thus any message + * that has been added since the last flush will not be + * reflected in the total. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/13/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +H5O_compute_size(H5F_t *f, H5O_t *oh, size_t *size_ptr) +{ + unsigned u; + size_t size; + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5O_compute_size, FAIL); + + /* check args */ + HDassert(f); + HDassert(oh); + HDassert(size_ptr); + + size = H5O_SIZEOF_HDR(f); + + for (u = 0; u < oh->nchunks; u++) + { + size += oh->chunk[u].size; + } + + HDassert(size >= H5O_SIZEOF_HDR(f)); + + *size_ptr = size; + +done: + + FUNC_LEAVE_NOAPI(ret_value); + +} /* H5O_compute_size() */ + + +/*------------------------------------------------------------------------- * Function: H5O_reset * * Purpose: Some message data structures have internal fields that diff --git a/src/H5Pdcpl.c b/src/H5Pdcpl.c index 99f3e30..33edafc 100644 --- a/src/H5Pdcpl.c +++ b/src/H5Pdcpl.c @@ -883,7 +883,8 @@ H5Pget_filter_by_id(hid_t plist_id, H5Z_filter_t id, unsigned int *flags/*out*/, name); /* Check args */ - if (cd_nelmts || cd_values) { + if (cd_nelmts || cd_values) +{ if (cd_nelmts && *cd_nelmts>256) /* * It's likely that users forget to initialize this on input, so diff --git a/src/H5err.txt b/src/H5err.txt index 802cccf..8b42b3c 100644 --- a/src/H5err.txt +++ b/src/H5err.txt @@ -107,6 +107,7 @@ MINOR, RESOURCE, H5E_ALREADYEXISTS, Object already exists MINOR, RESOURCE, H5E_CANTLOCK, Unable to lock object MINOR, RESOURCE, H5E_CANTUNLOCK, Unable to unlock object MINOR, RESOURCE, H5E_CANTGC, Unable to garbage collect +MINOR, RESOURCE, H5E_CANTGETSIZE, Unable to compute size # File accessability errors MINOR, FILEACC, H5E_FILEEXISTS, File already exists @@ -146,6 +147,11 @@ MINOR, CACHE, H5E_CANTSERIALIZE, Unable to serialize data from cache MINOR, CACHE, H5E_CANTLOAD, Unable to load metadata into cache MINOR, CACHE, H5E_PROTECT, Protected metadata error MINOR, CACHE, H5E_NOTCACHED, Metadata not currently cached +MINOR, CACHE, H5E_SYSTEM, Internal error detected +MINOR, CACHE, H5E_CANTINS, Unable to insert metadata into cache +MINOR, CACHE, H5E_CANTRENAME, Unable to rename metadata +MINOR, CACHE, H5E_CANTPROTECT, Unable to protect metadata +MINOR, CACHE, H5E_CANTUNPROTECT, Unable to unprotect metadata # B-tree related errors MINOR, BTREE, H5E_NOTFOUND, Object not found diff --git a/src/Makefile.in b/src/Makefile.in index 4dcb74a..dc59c33 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -30,7 +30,7 @@ LIB=libhdf5.la DISTCLEAN=libhdf5.settings ## Source and object files for the library (lexicographically)... -LIB_SRC=H5.c H5A.c H5AC.c H5B.c H5D.c H5Dcontig.c H5Dcompact.c H5Dio.c \ +LIB_SRC=H5.c H5A.c H5AC.c H5B.c H5C.c H5D.c H5Dcontig.c H5Dcompact.c H5Dio.c \ H5Distore.c H5Dseq.c H5Dtest.c H5E.c H5F.c H5Fdbg.c H5FD.c \ H5FDcore.c H5FDfamily.c H5FDfphdf5.c H5FDgass.c H5FDlog.c H5FDmpi.c \ H5FDmpio.c H5FDmpiposix.c H5FDmulti.c H5FDsec2.c H5FDsrb.c \ @@ -54,23 +54,25 @@ LIB_OBJ=$(LIB_SRC:.c=.lo) MOSTLYCLEAN=H5detect.o H5detect.lo H5detect H5Tinit.o H5Tinit.lo H5Tinit.c ## Public header files (to be installed)... -PUB_HDR=H5public.h H5Apublic.h H5ACpublic.h H5Bpublic.h H5Dpublic.h \ - H5Epubgen.h H5Epublic.h H5Fpublic.h H5FDpublic.h H5FDcore.h \ - H5FDfamily.h H5FDfphdf5.h H5FDgass.h H5FDlog.h H5FDmpi.h H5FDmpio.h \ - H5FDmpiposix.h H5FDmulti.h H5FDsec2.h H5FDsrb.h H5FDstdio.h \ - H5FDstream.h H5FPpublic.h H5Gpublic.h H5HGpublic.h H5HLpublic.h \ - H5Ipublic.h H5MMpublic.h H5Opublic.h H5Ppublic.h H5Rpublic.h \ - H5Spublic.h H5Tpublic.h H5Zpublic.h H5pubconf.h hdf5.h H5api_adpt.h +PUB_HDR=H5public.h H5Apublic.h H5ACpublic.h H5Bpublic.h H5Cpublic.h \ + H5Dpublic.h H5Epubgen.h H5Epublic.h H5Fpublic.h H5FDpublic.h \ + H5FDcore.h H5FDfamily.h H5FDfphdf5.h H5FDgass.h H5FDlog.h H5FDmpi.h \ + H5FDmpio.h H5FDmpiposix.h H5FDmulti.h H5FDsec2.h H5FDsrb.h \ + H5FDstdio.h H5FDstream.h H5FPpublic.h H5Gpublic.h H5HGpublic.h \ + H5HLpublic.h H5Ipublic.h H5MMpublic.h H5Opublic.h H5Ppublic.h \ + H5Rpublic.h H5Spublic.h H5Tpublic.h H5Zpublic.h H5pubconf.h hdf5.h \ + H5api_adpt.h ## Other header files (not to be installed)... PRIVATE_HDR=H5private.h H5Aprivate.h H5Apkg.h H5ACprivate.h H5Bprivate.h \ - H5Dprivate.h H5Edefin.h H5Einit.h H5Eprivate.h H5Eterm.h H5Fprivate.h \ - H5FDprivate.h H5FLprivate.h H5FOprivate.h H5FPprivate.h H5FSprivate.h \ - H5Gprivate.h H5Gpkg.h H5HGprivate.h H5HGpkg.h H5HLprivate.h H5HLpkg.h \ - H5HPprivate.h H5Iprivate.h H5MFprivate.h H5MMprivate.h H5Oprivate.h \ - H5Opkg.h H5Pprivate.h H5Ppkg.h H5Rprivate.h H5RSprivate.h \ - H5Sprivate.h H5STprivate.h H5Tprivate.h H5TBprivate.h H5Tpkg.h \ - H5TSprivate.h H5Vprivate.h H5Zprivate.h H5Zpkg.h H5config.h + H5Cprivate.h H5Dprivate.h H5Edefin.h H5Einit.h H5Eprivate.h H5Eterm.h \ + H5Fprivate.h H5FDprivate.h H5FLprivate.h H5FOprivate.h H5FPprivate.h \ + H5FSprivate.h H5Gprivate.h H5Gpkg.h H5HGprivate.h H5HGpkg.h \ + H5HLprivate.h H5HLpkg.h H5HPprivate.h H5Iprivate.h H5MFprivate.h \ + H5MMprivate.h H5Oprivate.h H5Opkg.h H5Pprivate.h H5Ppkg.h \ + H5Rprivate.h H5RSprivate.h H5Sprivate.h H5STprivate.h H5Tprivate.h \ + H5TBprivate.h H5Tpkg.h H5TSprivate.h H5Vprivate.h H5Zprivate.h \ + H5Zpkg.h H5config.h ## Error header generation ## |