diff options
author | Neil Fortner <nfortne2@hdfgroup.org> | 2011-02-08 17:32:56 (GMT) |
---|---|---|
committer | Neil Fortner <nfortne2@hdfgroup.org> | 2011-02-08 17:32:56 (GMT) |
commit | 0863d303729a1008b5954984adffa8223997fc2f (patch) | |
tree | cc273980f8c4a225cdde2100698b5300c2e060d2 /src | |
parent | f3ee1cc38d0604116afbffe72cd98dabfe1014a7 (diff) | |
download | hdf5-0863d303729a1008b5954984adffa8223997fc2f.zip hdf5-0863d303729a1008b5954984adffa8223997fc2f.tar.gz hdf5-0863d303729a1008b5954984adffa8223997fc2f.tar.bz2 |
[svn-r20064] Purpose: Implement external file cache
Description:
Implements a cache of files opened through external links. Adds the public
functions H5Pset_elink_file_cache_size(), H5Pget_elink_file_cache_size(), and
H5Frelease_file_cache().
Tested: jam, amani, heiwa (h5committest), fedora 64.
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/H5F.c | 62 | ||||
-rw-r--r-- | src/H5Fefc.c | 951 | ||||
-rw-r--r-- | src/H5Fpkg.h | 12 | ||||
-rw-r--r-- | src/H5Fprivate.h | 6 | ||||
-rw-r--r-- | src/H5Fpublic.h | 1 | ||||
-rw-r--r-- | src/H5Lexternal.c | 31 | ||||
-rw-r--r-- | src/H5Pfapl.c | 85 | ||||
-rw-r--r-- | src/H5Ppublic.h | 2 | ||||
-rwxr-xr-x | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/Makefile.in | 31 |
11 files changed, 1147 insertions, 39 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3330b8b..93370c0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -145,6 +145,7 @@ SET (H5F_SRCS ${HDF5_SRC_DIR}/H5Faccum.c ${HDF5_SRC_DIR}/H5Fdbg.c ${HDF5_SRC_DIR}/H5Fdeprec.c + ${HDF5_SRC_DIR}/H5Fefc.c ${HDF5_SRC_DIR}/H5Ffake.c ${HDF5_SRC_DIR}/H5Fio.c ${HDF5_SRC_DIR}/H5Fmount.c @@ -292,6 +292,7 @@ H5F_get_access_plist(H5F_t *f, hbool_t app_ref) H5P_genplist_t *new_plist; /* New property list */ H5P_genplist_t *old_plist; /* Old property list */ void *driver_info=NULL; + unsigned efc_size = 0; hid_t ret_value = SUCCEED; FUNC_ENTER_NOAPI(H5F_get_access_plist, FAIL) @@ -330,6 +331,10 @@ H5F_get_access_plist(H5F_t *f, hbool_t app_ref) HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set 'small data' cache size") if(H5P_set(new_plist, H5F_ACS_LATEST_FORMAT_NAME, &(f->shared->latest_format)) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set 'latest format' flag") + if(f->shared->efc) + efc_size = H5F_efc_max_nfiles(f->shared->efc); + if(H5P_set(new_plist, H5F_ACS_EFC_SIZE_NAME, &efc_size) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't set elink file cache size") /* * Since we're resetting the driver ID and info, close them if they @@ -840,6 +845,7 @@ H5F_new(H5F_file_t *shared, hid_t fcpl_id, hid_t fapl_id, H5FD_t *lf) } /* end if */ else { H5P_genplist_t *plist; /* Property list */ + unsigned efc_size; /* External file cache size */ size_t u; /* Local index variable */ HDassert(lf != NULL); @@ -902,6 +908,11 @@ H5F_new(H5F_file_t *shared, hid_t fcpl_id, hid_t fapl_id, H5FD_t *lf) if(H5P_get(plist, H5F_ACS_SDATA_BLOCK_SIZE_NAME, &(f->shared->sdata_aggr.alloc_size)) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get 'small data' cache size") f->shared->sdata_aggr.feature_flag = H5FD_FEAT_AGGREGATE_SMALLDATA; + if(H5P_get(plist, H5F_ACS_EFC_SIZE_NAME, &efc_size) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get elink file cache size") + if(efc_size > 0) + if(NULL == (f->shared->efc = H5F_efc_create(efc_size))) + HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "can't create external file cache") /* Get the VFD values to cache */ f->shared->maxaddr = H5FD_get_maxaddr(lf); @@ -998,6 +1009,13 @@ H5F_dest(H5F_t *f, hid_t dxpl_id, hbool_t flush) if(H5F_flush(f, dxpl_id) < 0) HDONE_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache") + /* Release the external file cache */ + if(f->shared->efc) { + if(H5F_efc_destroy(f->shared->efc) < 0) + HDONE_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "can't destroy external file cache") + f->shared->efc = NULL; + } /* end if */ + /* Release objects that depend on the superblock being initialized */ if(f->shared->sblock) { /* Shutdown file free space manager(s) */ @@ -1903,6 +1921,13 @@ H5F_try_close(H5F_t *f) if(H5F_close_mounts(f) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "can't unmount child files") + /* If there is more than one reference to the shared file struct and the + * file has an external file cache, we should see if it can be closed. This + * can happen if a cycle is formed with external file caches */ + if(f->shared->efc && (f->shared->nrefs > 1)) + if(H5F_efc_try_close(f) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "can't attempt to close EFC") + /* Delay flush until the shared file struct is closed, in H5F_dest. If the * application called H5Fclose, it would have been flushed in that function * (unless it will have been flushed in H5F_dest anyways). */ @@ -2929,3 +2954,40 @@ done: FUNC_LEAVE_API(ret_value) } /* end H5Fget_free_sections() */ + +/*------------------------------------------------------------------------- + * Function: H5Frelease_file_cache + * + * Purpose: Releases the external file cache associated with the + * provided file, potentially closing any cached files + * unless they are held open from somewhere\ else. + * + * Return: Success: non-negative + * Failure: negative + * + * Programmer: Neil Fortner; December 30, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t +H5Frelease_file_cache(hid_t file_id) +{ + H5F_t *file; /* File */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_API(H5Frelease_file_cache, FAIL) + H5TRACE1("e", "i", file_id); + + /* Check args */ + if(NULL == (file = (H5F_t *)H5I_object_verify(file_id, H5I_FILE))) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a file ID") + + /* Release the EFC */ + if(file->shared->efc) + if(H5F_efc_release(file->shared->efc) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "can't release external file cache") + +done: + FUNC_LEAVE_API(ret_value) +} /* end H5Frelease_file_cache() */ + diff --git a/src/H5Fefc.c b/src/H5Fefc.c new file mode 100644 index 0000000..eebf143 --- /dev/null +++ b/src/H5Fefc.c @@ -0,0 +1,951 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*------------------------------------------------------------------------- + * + * Created: H5Defc.c + * December 13, 2010 + * Neil Fortner <nfortne2@hdfgroup.org> + * + * Purpose: External file caching routines - implements a + * cache of external files to minimize the number of + * file opens and closes. + * + *------------------------------------------------------------------------- + */ + +#define H5F_PACKAGE /*suppress error about including H5Fpkg */ + + +/* Packages needed by this file... */ +#include "H5private.h" /* Generic Functions */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5Fpkg.h" /* File access */ +#include "H5MMprivate.h" /* Memory management */ +#include "H5Pprivate.h" /* Property lists */ + + +/* Special values for the "tag" field below */ +#define H5F_EFC_TAG_DEFAULT -1 +#define H5F_EFC_TAG_LOCK -2 +#define H5F_EFC_TAG_CLOSE -3 +#define H5F_EFC_TAG_DONTCLOSE -4 + +/* Structure for each entry in a file's external file cache */ +typedef struct H5F_efc_ent_t { + char *name; /* Name of the file */ + H5F_t *file; /* File object */ + struct H5F_efc_ent_t *LRU_next; /* Next item in LRU list */ + struct H5F_efc_ent_t *LRU_prev; /* Previous item in LRU list */ + unsigned nopen; /* Number of times this file is currently opened by an EFC client */ +} H5F_efc_ent_t; + +/* Structure for a shared file struct's external file cache */ +struct H5F_efc_t { + H5SL_t *slist; /* Skip list of cached external files */ + H5F_efc_ent_t *LRU_head; /* Head of LRU list. This is the least recently used file */ + H5F_efc_ent_t *LRU_tail; /* Tail of LRU list. This is the most recently used file */ + unsigned nfiles; /* Size of the external file cache */ + unsigned max_nfiles; /* Maximum size of the external file cache */ + unsigned nrefs; /* Number of times this file appears in another file's EFC */ + int tag; /* Temporary variable used by H5F_efc_try_close() */ + H5F_file_t *tmp_next; /* Next file in temporary list used by H5F_efc_try_close() */ +}; + +/* Private prototypes */ +static herr_t H5F_efc_remove_ent(H5F_efc_t *efc, H5F_efc_ent_t *ent); +static void H5F_efc_try_close_tag1(H5F_file_t *sf, H5F_file_t **tail); +static void H5F_efc_try_close_tag2(H5F_file_t *sf, H5F_file_t **tail); + +/* Free lists */ +H5FL_DEFINE_STATIC(H5F_efc_ent_t); +H5FL_DEFINE_STATIC(H5F_efc_t); + + +/*------------------------------------------------------------------------- + * Function: H5F_efc_create + * + * Purpose: Allocate and initialize a new external file cache object, + * which can the be used to cache open external files. + * the object must be freed with H5F_efc_destroy. + * + * Return: Pointer to new external file cache object on success + * NULL on failure + * + * Programmer: Neil Fortner + * Tuesday, December 14, 2010 + * + *------------------------------------------------------------------------- + */ +H5F_efc_t * +H5F_efc_create(unsigned max_nfiles) +{ + H5F_efc_t *efc = NULL; /* EFC object */ + H5F_efc_t *ret_value; /* Return value */ + + FUNC_ENTER_NOAPI(H5F_efc_create, NULL) + + /* Sanity checks */ + HDassert(max_nfiles > 0); + + /* Allocate EFC struct */ + if(NULL == (efc = H5FL_CALLOC(H5F_efc_t))) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") + + /* Initialize maximum number of files */ + efc->max_nfiles = max_nfiles; + + /* Initialize temporary ref count */ + efc->tag = H5F_EFC_TAG_DEFAULT; + + /* Set the return value */ + ret_value = efc; + +done: + if(ret_value == NULL && efc) + efc = H5FL_FREE(H5F_efc_t, efc); + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5F_efc_create() */ + + +/*------------------------------------------------------------------------- + * Function: H5F_efc_open + * + * Purpose: Opens a file using the external file cache. The target + * file is added to the external file cache of the parent + * if it is not already present. If the target file is in + * the parent's EFC, simply returns the target file. When + * the file object is no longer in use, it should be closed + * with H5F_efc_close (will not actually close the file + * until it is evicted from the EFC). + * + * Return: Pointer to open file on success + * NULL on failure + * + * Programmer: Neil Fortner + * Tuesday, December 14, 2010 + * + *------------------------------------------------------------------------- + */ +H5F_t * +H5F_efc_open(H5F_t *parent, const char *name, unsigned flags, hid_t fcpl_id, + hid_t fapl_id, hid_t dxpl_id) +{ + H5F_efc_t *efc = NULL; /* External file cache for parent file */ + H5F_efc_ent_t *ent = NULL; /* Entry for target file in efc */ + hbool_t open_file = FALSE; /* Whether ent->file needs to be closed in case of error */ + H5F_t *ret_value; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT(H5F_efc_open) + + /* Sanity checks */ + HDassert(parent); + HDassert(parent->shared); + HDassert(name); + + /* Get external file cache */ + efc = parent->shared->efc; + + /* Check if the EFC exists. If it does not, just call H5F_open(). We + * support this so clients do not have to make 2 different calls depending + * on the state of the efc. */ + if(!efc) { + if(NULL == (ret_value = H5F_open(name, flags, fcpl_id, fapl_id, + dxpl_id))) + HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "can't open file") + + /* Increment the number of open objects to prevent the file from being + * closed out from under us - "simulate" having an open file id. Note + * that this behaviour replaces the calls to H5F_incr_nopen_objs() and + * H5F_decr_nopen_objs() in H5L_extern_traverse(). */ + ret_value->nopen_objs++; + + HGOTO_DONE(ret_value) + } /* end if */ + + /* Search the skip list for name if the skip list exists, create the skip + * list otherwise */ + if(efc->slist) { + if(efc->nfiles > 0) + ent = (H5F_efc_ent_t *)H5SL_search(efc->slist, name); + } /* end if */ + else { + HDassert(efc->nfiles == 0); + if(NULL == (efc->slist = H5SL_create(H5SL_TYPE_STR))) + HGOTO_ERROR(H5E_FILE, H5E_CANTCREATE, NULL, "can't create skip list") + } /* end else */ + + /* If we found the file update the LRU list and return the cached file, + * otherwise open the file and cache it */ + if(ent) { + HDassert(efc->LRU_head); + HDassert(efc->LRU_tail); + + /* Move ent to the head of the LRU list, if it is not already there */ + if(ent->LRU_prev) { + HDassert(efc->LRU_head != ent); + + /* Remove from current position. Note that once we touch the LRU + * list we cannot revert to the previous state. Make sure there can + * be no errors between when we first touch the LRU list and when + * the cache is in a consistent state! */ + if(ent->LRU_next) + ent->LRU_next->LRU_prev = ent->LRU_prev; + else { + HDassert(efc->LRU_tail == ent); + efc->LRU_tail = ent->LRU_prev; + } /* end else */ + ent->LRU_prev->LRU_next = ent->LRU_next; + + /* Add to head of LRU list */ + ent->LRU_next = efc->LRU_head; + ent->LRU_next->LRU_prev = ent; + ent->LRU_prev = NULL; + efc->LRU_head = ent; + } /* end if */ + + /* Mark the file as open */ + ent->nopen++; + } /* end if */ + else { + /* Check if we need to evict something */ + if(efc->nfiles == efc->max_nfiles) { + /* Search for an unopened file from the tail */ + for(ent = efc->LRU_tail; ent && ent->nopen; ent = ent->LRU_prev); + + /* Evict the file if found, otherwise just open the target file and + * do not add it to cache */ + if(ent) { + if(H5F_efc_remove_ent(efc, ent) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTREMOVE, NULL, "can't remove entry from external file cache") + + /* Do not free ent, we will recycle it below */ + } /* end if */ + else { + /* Cannot cache file, just open file and return */ + if(NULL == (ret_value = H5F_open(name, flags, fcpl_id, fapl_id, + dxpl_id))) + HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "can't open file") + + /* Increment the number of open objects to prevent the file from + * being closed out from under us - "simulate" having an open + * file id */ + ret_value->nopen_objs++; + + HGOTO_DONE(ret_value) + } /* end else */ + } /* end if */ + else + /* Allocate new entry */ + if(NULL == (ent = H5FL_MALLOC(H5F_efc_ent_t))) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") + + /* Build new entry */ + if(NULL == (ent->name = H5MM_strdup(name))) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") + + /* Open the file */ + if(NULL == (ent->file = H5F_open(name, flags, fcpl_id, fapl_id, + dxpl_id))) + HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "can't open file") + open_file = TRUE; + + /* Increment the number of open objects to prevent the file from being + * closed out from under us - "simulate" having an open file id */ + ent->file->nopen_objs++; + + /* Add the file to the cache */ + /* Skip list */ + if(H5SL_insert(efc->slist, ent, ent->name) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTINSERT, NULL, "can't insert entry into skip list") + + /* Add to head of LRU list and update tail if necessary */ + ent->LRU_next = efc->LRU_head; + if(ent->LRU_next) + ent->LRU_next->LRU_prev = ent; + ent->LRU_prev = NULL; + efc->LRU_head = ent; + if(!efc->LRU_tail) { + HDassert(!ent->LRU_next); + efc->LRU_tail = ent; + } /* end if */ + + /* Mark the file as open */ + ent->nopen = 1; + + /* Update nfiles and nrefs */ + efc->nfiles++; + if(ent->file->shared->efc) + ent->file->shared->efc->nrefs++; + } /* end else */ + + HDassert(ent); + HDassert(ent->file); + HDassert(ent->name); + HDassert(ent->nopen); + + /* Set the return value */ + ret_value = ent->file; + +done: + if(!ret_value) + if(ent) { + if(open_file) { + ent->file->nopen_objs--; + if(H5F_try_close(ent->file) < 0) + HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, NULL, "can't close external file") + } /* end if */ + ent->name = (char *)H5MM_xfree(ent->name); + ent = H5FL_FREE(H5F_efc_ent_t, ent); + } /* end if */ + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5F_efc_open() */ + + +/*------------------------------------------------------------------------- + * Function: H5F_efc_close + * + * Purpose: Closes (unlocks) a file opened using the external file + * cache. The target file is not immediately closed unless + * there is no external file cache for the parent file. + * + * Return: Non-negative on success + * Negative on failure + * + * Programmer: Neil Fortner + * Wednesday, December 15, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t +H5F_efc_close(H5F_t *parent, H5F_t *file) +{ + H5F_efc_t *efc = NULL; /* External file cache for parent file */ + H5F_efc_ent_t *ent = NULL; /* Entry for target file in efc */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT(H5F_efc_close) + + /* Sanity checks */ + HDassert(parent); + HDassert(parent->shared); + HDassert(file); + HDassert(file->shared); + + /* Get external file cache */ + efc = parent->shared->efc; + + /* Check if the EFC exists. If it does not, just call H5F_try_close(). We + * support this so clients do not have to make 2 different calls depending + * on the state of the efc. */ + if(!efc) { + file->nopen_objs--; + if(H5F_try_close(file) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "can't close external file") + + HGOTO_DONE(SUCCEED) + } /* end if */ + + /* Scan the parent's LRU list from the head to file file. We do this + * instead of a skip list lookup because the file will almost always be at + * the head. In the unlikely case that the file is not found, just call + * H5F_try_close(). This could happen if the EFC was full of open files + * when the file was opened. */ + for(ent = efc->LRU_head; ent && ent->file != file; ent = ent->LRU_next); + if(!ent) { + file->nopen_objs--; + if(H5F_try_close(file) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "can't close external file") + } /* end if */ + else + /* Reduce the open count on this entry */ + ent->nopen--; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5F_efc_close() */ + + +/*------------------------------------------------------------------------- + * Function: H5F_efc_max_nfiles + * + * Purpose: Returns the maximum number of files in the provided + * external file cache. + * + * Return: Maximum number of files (never fails) + * + * Programmer: Neil Fortner + * Wednesday, December 15, 2010 + * + *------------------------------------------------------------------------- + */ +unsigned +H5F_efc_max_nfiles(H5F_efc_t *efc) +{ + FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5F_efc_max_nfiles) + + HDassert(efc); + HDassert(efc->max_nfiles > 0); + + FUNC_LEAVE_NOAPI(efc->max_nfiles) +} /* end H5F_efc_max_nfiles */ + + +/*------------------------------------------------------------------------- + * Function: H5F_efc_release + * + * Purpose: Releases the external file cache, potentially closing any + * cached files unless they are held open from somewhere + * else (or are currently opened by a client). + * + * Return: Non-negative on success + * Negative on failure + * + * Programmer: Neil Fortner + * Wednesday, December 15, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t +H5F_efc_release(H5F_efc_t *efc) +{ + H5F_efc_ent_t *ent = NULL; /* EFC entry */ + H5F_efc_ent_t *prev_ent = NULL; /* Previous EFC entry */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT(H5F_efc_release) + + /* Sanity checks */ + HDassert(efc); + + /* Lock the EFC to prevent manipulation of the EFC wile we are releasing it. + * The EFC should never be locked when we enter this function because that + * would require a cycle, a cycle would necessarily invoke + * H5F_efc_try_close(), and that function checks the status of the lock + * before calling this one. */ + HDassert((efc->tag == H5F_EFC_TAG_DEFAULT) + || (efc->tag == H5F_EFC_TAG_CLOSE)); + efc->tag = H5F_EFC_TAG_LOCK; + + /* Walk down the LRU list, releasing any files that are not opened by an EFC + * client */ + ent = efc->LRU_head; + while(ent) + if(!ent->nopen) { + if(H5F_efc_remove_ent(efc, ent) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTREMOVE, FAIL, "can't remove entry from external file cache") + + /* Free the entry and move to next entry in LRU list */ + prev_ent = ent; + ent = ent->LRU_next; + prev_ent = H5FL_FREE(H5F_efc_ent_t, prev_ent); + } /* end if */ + else + /* Can't release file because it's open; just advance the pointer */ + ent = ent->LRU_next; + + /* Reset tag. No need to reset to CLOSE if that was the original tag, as in + * that case the file must be getting closed anyways. */ + efc->tag = H5F_EFC_TAG_DEFAULT; + +done: + FUNC_LEAVE_NOAPI(ret_value); +} /* end H5F_efc_release() */ + + +/*------------------------------------------------------------------------- + * Function: H5F_efc_destroy + * + * Purpose: Frees an external file cache object, releasing it first + * if necessary. If it cannot be fully released, for example + * if there are open files, returns an error. + * + * Return: Non-negative on success + * Negative on failure + * + * Programmer: Neil Fortner + * Wednesday, December 15, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t +H5F_efc_destroy(H5F_efc_t *efc) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT(H5F_efc_destroy) + + /* Sanity checks */ + HDassert(efc); + + if(efc->nfiles > 0) { + /* Release (clear) the efc */ + if(H5F_efc_release(efc) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "can't release external file cache") + + /* If there are still cached files, return an error */ + if(efc->nfiles > 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTFREE, FAIL, "can't destroy EFC after incomplete release") + } /* end if */ + + HDassert(efc->nfiles == 0); + HDassert(efc->LRU_head == NULL); + HDassert(efc->LRU_tail == NULL); + + /* Close skip list */ + if(efc->slist) + if(H5SL_close(efc->slist) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTFREE, FAIL, "can't close skip list") + + /* Free EFC object */ + (void)H5FL_FREE(H5F_efc_t, efc); + +done: + FUNC_LEAVE_NOAPI(ret_value); +} /* end H5F_efc_destroy() */ + + +/*------------------------------------------------------------------------- + * Function: H5F_efc_remove_ent + * + * Purpose: Removes the specified entry from the specified EFC, + * closing the file if requested. Does not free the entry. + * + * Return: Non-negative on success + * Negative on failure + * + * Programmer: Neil Fortner + * Wednesday, December 15, 2010 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5F_efc_remove_ent(H5F_efc_t *efc, H5F_efc_ent_t *ent) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT(H5F_efc_remove_ent) + + /* Sanity checks */ + HDassert(efc); + HDassert(efc->slist); + HDassert(ent); + + /* Remove from skip list */ + if(ent != H5SL_remove(efc->slist, ent->name)) + HGOTO_ERROR(H5E_FILE, H5E_CANTDELETE, FAIL, "can't delete entry from skip list") + + /* Remove from LRU list */ + if(ent->LRU_next) + ent->LRU_next->LRU_prev = ent->LRU_prev; + else { + HDassert(efc->LRU_tail == ent); + efc->LRU_tail = ent->LRU_prev; + } /* end else */ + if(ent->LRU_prev) + ent->LRU_prev->LRU_next = ent->LRU_next; + else { + HDassert(efc->LRU_head == ent); + efc->LRU_head = ent->LRU_next; + } /* end else */ + + /* Update nfiles and nrefs */ + efc->nfiles--; + if(ent->file->shared->efc) + ent->file->shared->efc->nrefs--; + + /* Free the name */ + ent->name = (char *)H5MM_xfree(ent->name); + + /* Close the file. Note that since H5F_t structs returned from H5F_open() + * are *always* unique, there is no need to reference count this struct. + * However we must still manipulate the nopen_objs field to prevent the file + * from being closed out from under us. */ + ent->file->nopen_objs--; + if(H5F_try_close(ent->file) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "can't close external file") + ent->file = NULL; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5F_efc_remove_ent() */ + + +/*------------------------------------------------------------------------- + * Function: H5F_efc_try_close_tag1 + * + * Purpose: Recursively traverse the EFC tree, keeping a temporary + * reference count on each file that assumes all reachable + * files will eventually be closed. + * + * Return: void (never fails) + * + * Programmer: Neil Fortner + * Monday, January 10, 2011 + * + *------------------------------------------------------------------------- + */ +static void +H5F_efc_try_close_tag1(H5F_file_t *sf, H5F_file_t **tail) +{ + H5F_efc_ent_t *ent = NULL; /* EFC entry */ + H5F_file_t *esf; /* Convenience pointer to ent->file->shared */ + + FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5F_efc_try_close_tag1) + + /* Sanity checks */ + HDassert(sf); + HDassert(sf->efc); + HDassert((sf->efc->tag > 0) || (sf->nrefs == sf->efc->nrefs)); + HDassert(sf->efc->tag != H5F_EFC_TAG_LOCK); + HDassert(tail); + HDassert(*tail); + + /* Recurse into this file's cached files */ + for(ent = sf->efc->LRU_head; ent; ent = ent->LRU_next) { + esf = ent->file->shared; + + if(esf->efc) { + /* If tag were 0, that would mean there are more actual references + * than are counted by nrefs */ + HDassert(esf->efc->tag != 0); + + /* If tag has been set, we have already visited this file so just + * decrement tag and continue */ + if(esf->efc->tag > 0) + esf->efc->tag--; + /* If there are references that are not from an EFC, it will never + * be possible to close the file. Just continue. Also continue if + * the EFC is locked or the file is open (through the EFC). Note + * that the reference counts will never match for the root file, but + * that's ok because the root file will always have a tag and enter + * the branch above. */ + else if((esf->nrefs == esf->efc->nrefs) + && (esf->efc->tag != H5F_EFC_TAG_LOCK) && !(ent->nopen)) { + /* If we get here, this file's "tmp_next" pointer must be NULL + */ + HDassert(esf->efc->tmp_next == NULL); + + /* If nrefs > 1, Add this file to the list of files with nrefs > + * 1 and initialize tag to the number of references (except this + * one) */ + if(esf->nrefs > 1) { + (*tail)->efc->tmp_next = esf; + *tail = esf; + esf->efc->tag = (int)esf->nrefs - 1; + } /* end if */ + + /* Recurse into the entry */ + H5F_efc_try_close_tag1(ent->file->shared, tail); + } /* end if */ + } /* end if */ + } /* end for */ + + FUNC_LEAVE_NOAPI_VOID +} /* end H5F_efc_try_close_tag1() */ + + +/*------------------------------------------------------------------------- + * Function: H5F_efc_try_close_tag2 + * + * Purpose: Recuresively mark all files reachable through this one as + * uncloseable, and add newly uncloseable files to the tail + * of the provided linked list. + * + * Return: void (never fails) + * + * Programmer: Neil Fortner + * Monday, January 10, 2011 + * + *------------------------------------------------------------------------- + */ +static void +H5F_efc_try_close_tag2(H5F_file_t *sf, H5F_file_t **tail) +{ + H5F_efc_ent_t *ent = NULL; /* EFC entry */ + H5F_file_t *esf; /* Convenience pointer to ent->file->shared */ + + FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5F_efc_try_close_tag2) + + /* Sanity checks */ + HDassert(sf); + HDassert(sf->efc); + + /* Recurse into this file's cached files */ + for(ent = sf->efc->LRU_head; ent; ent = ent->LRU_next) { + esf = ent->file->shared; + + /* Only recurse if the file is tagged CLOSE or DEFAULT. If it is tagged + * DONTCLOSE, we have already visited this file *or* it will be the + * start point of another iteration. No files should be tagged with a + * nonegative value at this point. If it is tagged as DEFAULT, we must + * apply the same conditions as in cb1 above for recursion in order to + * make sure we do not go off into somewhere cb1 didn't touch. The + * root file should never be tagged DEFAULT here, so the reference check + * is still appropriate. */ + if((esf->efc) && ((esf->efc->tag == H5F_EFC_TAG_CLOSE) + || ((esf->efc->tag == H5F_EFC_TAG_DEFAULT) + && (esf->nrefs == esf->efc->nrefs) && !(ent->nopen)))) { + /* tag should always be CLOSE is nrefs > 1 or DEFAULT if nrefs == 1 + * here */ + HDassert(((esf->nrefs > 1) + && ((esf->efc->tag == H5F_EFC_TAG_CLOSE))) + || ((esf->nrefs == 1) + && (esf->efc->tag == H5F_EFC_TAG_DEFAULT))); + + /* If tag is set to DONTCLOSE, we have already visited this file + * *or* it will be the start point of another iteration so just + * continue */ + if(esf->efc->tag != H5F_EFC_TAG_DONTCLOSE) { + /* If tag is CLOSE, set to DONTCLOSE and add to the list of + * uncloseable files. */ + if(esf->efc->tag == H5F_EFC_TAG_CLOSE) { + esf->efc->tag = H5F_EFC_TAG_DONTCLOSE; + esf->efc->tmp_next = NULL; + (*tail)->efc->tmp_next = esf; + *tail = esf; + } /* end if */ + + /* Recurse into the entry */ + H5F_efc_try_close_tag2(esf, tail); + } /* end if */ + } /* end if */ + } /* end for */ + + FUNC_LEAVE_NOAPI_VOID +} /* end H5F_efc_try_close_tag2() */ + + +/*------------------------------------------------------------------------- + * Function: H5F_efc_try_close + * + * Purpose: Attempts to close the provided (shared) file by checking + * to see if the releasing the EFC would cause its reference + * count to drop to 0. Necessary to handle the case where + * chained EFCs form a cycle. Note that this function does + * not actually close the file (though it closes all children + * as appropriate), as that is left up to the calling + * function H5F_try_close(). + * + * Because H5F_try_close() has no way of telling if it is + * called recursively from within this function, this + * function serves as both the root of iteration and the + * "callback" for the final pass (the one where the files are + * actually closed). The code for the callback case is at + * the top of this function; luckily it only consists of a + * (possible) call to H5F_efc_release(). + * + * The algorithm basically consists of 3 passes over the EFC + * tree. The first pass assumes that every reachable file is + * closed, and keeps track of what the final reference count + * would be for every reachable file. The files are then + * tagged as either closeable or uncloseable based on whether + * this reference count drops to 0. + * + * The second pass initiates a traversal from each file + * marked as uncloseable in the first pass, and marks every + * file reachable from the initial uncloseable file as + * uncloseable. This eliminates files that were marked as + * closeable only because the first pass assumed that an + * uncloseable file would be closed. + * + * The final pass exploits the H5F_efc_release()-> + * H5F_efc_remove_ent()->H5F_try_close()->H5F_efc_try_close() + * calling chain to recursively close the tree, but only the + * files that are still marked as closeable. All files + * marked as closeable have their EFCs released, and will + * eventually be closed when their last parent EFC is + * released (the last part is guaranteed to be true by the + * first 2 passes). + * + * Return: Non-negative on success + * Negative on failure + * + * Programmer: Neil Fortner + * Thursday, January 6, 2011 + * + *------------------------------------------------------------------------- + */ +herr_t +H5F_efc_try_close(H5F_t *f) +{ + H5F_file_t *tail; /* Tail of linked list of found files. Head will be f->shared. */ + H5F_file_t *uncloseable_head = NULL; /* Head of linked list of files found to be uncloseable by the first pass */ + H5F_file_t *uncloseable_tail = NULL; /* Tail of linked list of files found to be uncloseable by the first pass */ + H5F_file_t *sf; /* Temporary file pointer */ + H5F_file_t *next; /* Temporary file pointer */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT(H5F_efc_try_close) + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(f->shared->efc); + HDassert(f->shared->nrefs > f->shared->efc->nrefs); + HDassert(f->shared->nrefs > 1); + HDassert(f->shared->efc->tag < 0); + + if(f->shared->efc->tag == H5F_EFC_TAG_CLOSE) { + /* We must have reentered this function, and we should close this file. + * In actuality, we just release the EFC, the recursion should + * eventually reduce this file's reference count to 1 (though possibly + * not from this call to H5F_efc_release()). */ + if(H5F_efc_release(f->shared->efc) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "can't release external file cache") + + /* If we marked the file as closeable, there must be no open files in + * its EFC. This is because, in order to close an open child file, the + * client must keep a copy of the parent file open. The algorithm + * detect that the parent file is open (directly or through an EFC) and + * refuse to close it. Verify that all files were released from this + * EFC (i.e. none were open). */ + HDassert(f->shared->efc->nfiles == 0); + + HGOTO_DONE(SUCCEED) + } /* end if */ + + /* Conditions where we should not do anything and just return immediately */ + /* If there are references that are not from an EFC or f, it will never + * be possible to close the file. Just return. Note that this holds true + * for the case that this file is being closed through H5F_efc_release() + * because that function (through H5F_efc_remove_ent()) decrements the EFC + * reference count before it calls H5F_try_close(). This may occur if this + * function is reentered. */ + /* If the tag is H5F_EFC_TAG_DONTCLOSE, then we have definitely reentered + * this function, and this file has been marked as uncloseable, so we should + * not close/release it */ + /* If nfiles is 0, then there is nothing to do. Just return. This may also + * occur on reentry (for example if this file was previously released). */ + if((f->shared->nrefs != f->shared->efc->nrefs + 1) + || (f->shared->efc->tag == H5F_EFC_TAG_DONTCLOSE) + || (f->shared->efc->nfiles == 0)) + /* We must have reentered this function, and we should not close this + * file. Just return. */ + HGOTO_DONE(SUCCEED) + + /* If the file EFC were locked, that should always mean that there exists + * a reference to this file that is not in an EFC (it may have just been + * removed from an EFC), and should have been caught by the above check */ + /* If we get here then we must be beginning a new run. Make sure that the + * temporary variables in f->shared->efc are at the default value */ + HDassert(f->shared->efc->tag == H5F_EFC_TAG_DEFAULT); + HDassert(f->shared->efc->tmp_next == NULL); + + /* Set up linked list for traversal into EFC tree. f->shared is guaranteed + * to always be at the head. */ + tail = f->shared; + + /* Set up temporary reference count on root file */ + f->shared->efc->tag = (int)f->shared->efc->nrefs; + + /* First Pass: simulate closing all files reachable from this one, use "tag" + * field to keep track of final reference count for each file (including + * this one). Keep list of files with starting reference count > 1 (head is + * f->shared). */ + H5F_efc_try_close_tag1(f->shared, &tail); + + /* Check if f->shared->efc->tag dropped to 0. If it did not, + * we cannot close anything. Just reset temporary values and return. */ + if(f->shared->efc->tag > 0) { + sf = f->shared; + while(sf) { + next = sf->efc->tmp_next; + sf->efc->tag = H5F_EFC_TAG_DEFAULT; + sf->efc->tmp_next = NULL; + sf = next; + } /* end while */ + HGOTO_DONE(SUCCEED) + } /* end if */ + + /* Run through the linked list , separating into two lists, one with tag == + * 0 and one with tag > 0. Mark them as either H5F_EFC_TAG_CLOSE or + * H5F_EFC_TAG_DONTCLOSE as appropriate. */ + sf = f->shared; + tail = NULL; + while(sf) { + HDassert(sf->efc->tag >= 0); + next = sf->efc->tmp_next; + if(sf->efc->tag > 0) { + /* Remove from main list */ + HDassert(tail); + tail->efc->tmp_next = sf->efc->tmp_next; + sf->efc->tmp_next = NULL; + + /* Add to uncloseable list */ + if(!uncloseable_head) + uncloseable_head = sf; + else + uncloseable_tail->efc->tmp_next = sf; + uncloseable_tail = sf; + + /* Mark as uncloseable */ + sf->efc->tag = H5F_EFC_TAG_DONTCLOSE; + } /* end if */ + else { + sf->efc->tag = H5F_EFC_TAG_CLOSE; + tail = sf; + } /* end else */ + sf = next; + } /* end while */ + + /* Second pass: Determine which of the reachable files found in pass 1 + * cannot be closed by releasing the root file's EFC. Run through the + * uncloseable list, for each item traverse the files reachable through the + * EFC, mark the file as uncloseable, and add it to the list of uncloseable + * files (for cleanup). Use "tail" to store the original uncloseable tail + * so we know when to stop. We do not need to keep track of the closeable + * list any more. */ + sf = uncloseable_head; + if(sf) { + tail = uncloseable_tail; + HDassert(tail); + while(sf != tail->efc->tmp_next) { + H5F_efc_try_close_tag2(sf, &uncloseable_tail); + sf = sf->efc->tmp_next; + } /* end while */ + } /* end if */ + + /* If the root file's tag is still H5F_EFC_TAG_CLOSE, release its EFC. This + * should start the recursive release that should close all closeable files. + * Also, see the top of this function. */ + if(f->shared->efc->tag == H5F_EFC_TAG_CLOSE) { + if(H5F_efc_release(f->shared->efc) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "can't release external file cache") + + /* Make sure the file's reference count is now 1 and will be closed by + * H5F_dest(). */ + HDassert(f->shared->nrefs == 1); + } /* end if */ + + /* Clean up uncloseable files (reset tag and tmp_next). All closeable files + * should have been closed, and therefore do not need to be cleaned up. */ + if(uncloseable_head) { + sf = uncloseable_head; + while(sf) { + next = sf->efc->tmp_next; + HDassert(sf->efc->tag == H5F_EFC_TAG_DONTCLOSE); + sf->efc->tag = H5F_EFC_TAG_DEFAULT; + sf->efc->tmp_next = NULL; + sf = next; + } /* end while */ + } /* end if */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5F_efc_try_close() */ + diff --git a/src/H5Fpkg.h b/src/H5Fpkg.h index c98bda4..f63fa98 100644 --- a/src/H5Fpkg.h +++ b/src/H5Fpkg.h @@ -127,6 +127,10 @@ + H5F_SUPERBLOCK_VARLEN_SIZE(v, f)) +/* Forward declaration external file cache struct used below (defined in + * H5Fefc.c) */ +typedef struct H5F_efc_t H5F_efc_t; + /* Structure for metadata & "small [raw] data" block aggregation fields */ struct H5F_blk_aggr_t { unsigned long feature_flag; /* Feature flag type */ @@ -200,6 +204,7 @@ typedef struct H5F_file_t { unsigned nrefs; /* Ref count for times file is opened */ unsigned flags; /* Access Permissions for file */ H5F_mtab_t mtab; /* File mount table */ + H5F_efc_t *efc; /* External file cache */ /* Cached values from FCPL/superblock */ uint8_t sizeof_addr; /* Size of addresses in file */ @@ -328,6 +333,13 @@ H5_DLL herr_t H5F_sfile_add(H5F_file_t *shared); H5_DLL H5F_file_t * H5F_sfile_search(H5FD_t *lf); H5_DLL herr_t H5F_sfile_remove(H5F_file_t *shared); +/* External file cache routines */ +H5_DLL H5F_efc_t *H5F_efc_create(unsigned max_nfiles); +H5_DLL unsigned H5F_efc_max_nfiles(H5F_efc_t *efc); +H5_DLL herr_t H5F_efc_release(H5F_efc_t *efc); +H5_DLL herr_t H5F_efc_destroy(H5F_efc_t *efc); +H5_DLL herr_t H5F_efc_try_close(H5F_t *f); + /* Testing functions */ #ifdef H5F_TESTING H5_DLL herr_t H5F_get_sohm_mesg_count_test(hid_t fid, unsigned type_id, diff --git a/src/H5Fprivate.h b/src/H5Fprivate.h index 10c2976..22d9acd 100644 --- a/src/H5Fprivate.h +++ b/src/H5Fprivate.h @@ -381,6 +381,7 @@ typedef struct H5F_blk_aggr_t H5F_blk_aggr_t; #define H5F_ACS_MULTI_TYPE_NAME "multi_type" /* Data type in multi file driver */ #define H5F_ACS_LATEST_FORMAT_NAME "latest_format" /* 'Use latest format version' flag */ #define H5F_ACS_WANT_POSIX_FD_NAME "want_posix_fd" /* Internal: query the file descriptor from the core VFD, instead of the memory address */ +#define H5F_ACS_EFC_SIZE_NAME "efc_size" /* Size of external file cache */ /* ======================== File Mount properties ====================*/ #define H5F_MNT_SYM_LOCAL_NAME "local" /* Whether absolute symlinks local to file. */ @@ -550,6 +551,11 @@ H5_DLL MPI_Comm H5F_mpi_get_comm(const H5F_t *f); H5_DLL int H5F_mpi_get_size(const H5F_t *f); #endif /* H5_HAVE_PARALLEL */ +/* External file cache routines */ +H5_DLL H5F_t *H5F_efc_open(H5F_t *parent, const char *name, unsigned flags, + hid_t fcpl_id, hid_t fapl_id, hid_t dxpl_id); +H5_DLL herr_t H5F_efc_close(H5F_t *parent, H5F_t *file); + /* Debugging functions */ H5_DLL herr_t H5F_debug(H5F_t *f, FILE * stream, int indent, int fwidth); diff --git a/src/H5Fpublic.h b/src/H5Fpublic.h index 5ca64dc..d87dae6 100644 --- a/src/H5Fpublic.h +++ b/src/H5Fpublic.h @@ -198,6 +198,7 @@ H5_DLL ssize_t H5Fget_name(hid_t obj_id, char *name, size_t size); H5_DLL herr_t H5Fget_info2(hid_t obj_id, H5F_info2_t *finfo); H5_DLL ssize_t H5Fget_free_sections(hid_t file_id, H5F_mem_t type, size_t nsects, H5F_sect_info_t *sect_info/*out*/); +H5_DLL herr_t H5Frelease_file_cache(hid_t file_id); /* Symbols defined for compatibility with previous versions of the HDF5 API. * diff --git a/src/H5Lexternal.c b/src/H5Lexternal.c index eb2ffe5..03f2e15 100644 --- a/src/H5Lexternal.c +++ b/src/H5Lexternal.c @@ -322,7 +322,7 @@ H5L_extern_traverse(const char UNUSED *link_name, hid_t cur_group, /* target file_name is an absolute pathname: see RM for detailed description */ if(CHECK_ABSOLUTE(file_name) || CHECK_ABS_PATH(file_name)) { /* Try opening file */ - if(NULL == (ext_file = H5F_open(file_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) { + if(NULL == (ext_file = H5F_efc_open(loc.oloc->file, file_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) { char *ptr = NULL; H5E_clear_stack(NULL); @@ -334,7 +334,7 @@ H5L_extern_traverse(const char UNUSED *link_name, hid_t cur_group, } /* end if */ } /* end if */ else if(CHECK_ABS_DRIVE(file_name)) { - if(NULL == (ext_file = H5F_open(file_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) { + if(NULL == (ext_file = H5F_efc_open(loc.oloc->file, file_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) { H5E_clear_stack(NULL); /* strip "<drive-letter>:" */ HDstrcpy(temp_file_name, &file_name[2]); @@ -361,7 +361,7 @@ H5L_extern_traverse(const char UNUSED *link_name, hid_t cur_group, HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "can't prepend prefix to filename") } - ext_file = H5F_open(full_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id); + ext_file = H5F_efc_open(loc.oloc->file, full_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id); full_name = (char *)H5MM_xfree(full_name); if(ext_file != NULL) break; @@ -379,7 +379,7 @@ H5L_extern_traverse(const char UNUSED *link_name, hid_t cur_group, if(my_prefix) { if(H5L_build_name(my_prefix, temp_file_name, &full_name/*out*/) < 0) HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "can't prepend prefix to filename") - if(NULL == (ext_file = H5F_open(full_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) + if(NULL == (ext_file = H5F_efc_open(loc.oloc->file, full_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) H5E_clear_stack(NULL); full_name = (char *)H5MM_xfree(full_name); } /* end if */ @@ -392,7 +392,7 @@ H5L_extern_traverse(const char UNUSED *link_name, hid_t cur_group, if(NULL != (extpath = H5F_EXTPATH(loc.oloc->file))) { if(H5L_build_name(extpath, temp_file_name, &full_name/*out*/) < 0) HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "can't prepend prefix to filename") - if(NULL == (ext_file = H5F_open(full_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) + if(NULL == (ext_file = H5F_efc_open(loc.oloc->file, full_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) H5E_clear_stack(NULL); full_name = (char *)H5MM_xfree(full_name); } /* end if */ @@ -400,7 +400,7 @@ H5L_extern_traverse(const char UNUSED *link_name, hid_t cur_group, /* try the relative file_name stored in temp_file_name */ if(ext_file == NULL) { - if(NULL == (ext_file = H5F_open(temp_file_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) + if(NULL == (ext_file = H5F_efc_open(loc.oloc->file, temp_file_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) H5E_clear_stack(NULL); } /* end if */ @@ -427,32 +427,19 @@ H5L_extern_traverse(const char UNUSED *link_name, hid_t cur_group, HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "can't prepend prefix to filename") /* Try opening with the resolved name */ - if(NULL == (ext_file = H5F_open(full_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) + if(NULL == (ext_file = H5F_efc_open(loc.oloc->file, full_name, intent, H5P_FILE_CREATE_DEFAULT, fapl_id, H5AC_dxpl_id))) HGOTO_ERROR(H5E_LINK, H5E_CANTOPENFILE, FAIL, "unable to open external file, external link file name = '%s', temp_file_name = '%s'", file_name, temp_file_name) full_name = (char *)H5MM_xfree(full_name); } /* end if */ - /* Increment the number of open objects, to hold the file open */ - H5F_incr_nopen_objs(ext_file); - /* Retrieve the "group location" for the file's root group */ if(H5G_loc_root(ext_file, &root_loc) < 0) HGOTO_ERROR(H5E_SYM, H5E_BADVALUE, FAIL, "unable to create location for file") /* Open the object referenced in the external file */ - if((ext_obj = H5O_open_name(&root_loc, obj_name, lapl_id, FALSE)) < 0) { - H5F_decr_nopen_objs(ext_file); + if((ext_obj = H5O_open_name(&root_loc, obj_name, lapl_id, FALSE)) < 0) HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to open object") - } /* end if */ - - /* Decrement the number of open objects, to let the file close */ - H5F_decr_nopen_objs(ext_file); - - /* Close the external file */ - if(H5F_try_close(ext_file) < 0) - HGOTO_ERROR(H5E_LINK, H5E_CANTCLOSEFILE, FAIL, "problem closing external file") - ext_file = NULL; /* Set return value */ ret_value = ext_obj; @@ -461,7 +448,7 @@ done: /* Release resources */ if(fapl_id > 0 && H5I_dec_ref(fapl_id) < 0) HDONE_ERROR(H5E_ATOM, H5E_CANTRELEASE, FAIL, "unable to close atom for file access property list") - if(ext_file && H5F_try_close(ext_file) < 0) + if(ext_file && H5F_efc_close(loc.oloc->file, ext_file) < 0) HDONE_ERROR(H5E_LINK, H5E_CANTCLOSEFILE, FAIL, "problem closing external file") if(parent_group_name && parent_group_name != local_group_name) parent_group_name = (char *)H5MM_xfree(parent_group_name); diff --git a/src/H5Pfapl.c b/src/H5Pfapl.c index 10922da..7dea0b2 100644 --- a/src/H5Pfapl.c +++ b/src/H5Pfapl.c @@ -119,6 +119,9 @@ */ #define H5F_ACS_WANT_POSIX_FD_SIZE sizeof(hbool_t) #define H5F_ACS_WANT_POSIX_FD_DEF FALSE +/* Definition for external file cache size */ +#define H5F_ACS_EFC_SIZE_SIZE sizeof(unsigned) +#define H5F_ACS_EFC_SIZE_DEF 0 /******************/ @@ -211,6 +214,7 @@ H5P_facc_reg_prop(H5P_genclass_t *pclass) H5FD_mem_t mem_type = H5F_ACS_MULTI_TYPE_DEF; /* Default file space type for multi VFD */ hbool_t latest_format = H5F_ACS_LATEST_FORMAT_DEF; /* Default setting for "use the latest version of the format" flag */ hbool_t want_posix_fd = H5F_ACS_WANT_POSIX_FD_DEF; /* Default setting for retrieving 'handle' from core VFD */ + unsigned efc_size = H5F_ACS_EFC_SIZE_DEF; /* Default external file cache size */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI_NOINIT(H5P_facc_reg_prop) @@ -292,6 +296,10 @@ H5P_facc_reg_prop(H5P_genclass_t *pclass) if(H5P_register_real(pclass, H5F_ACS_WANT_POSIX_FD_NAME, H5F_ACS_WANT_POSIX_FD_SIZE, &want_posix_fd, NULL, NULL, NULL, NULL, NULL, NULL, NULL) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class") + /* Register the external file cache size */ + if(H5P_register_real(pclass, H5F_ACS_EFC_SIZE_NAME, H5F_ACS_EFC_SIZE_SIZE, &efc_size, NULL, NULL, NULL, NULL, NULL, NULL, NULL) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class") + done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5P_facc_reg_prop() */ @@ -2016,3 +2024,80 @@ done: FUNC_LEAVE_API(ret_value) } /* end H5Pget_libver_bounds() */ + +/*------------------------------------------------------------------------- + * Function: H5Pset_elink_file_cache_size + * + * Purpose: Sets the number of files opened through external links + * from the file associated with this fapl to be held open + * in that file's external file cache. When the maximum + * number of files is reached, the least recently used file + * is closed (unless it is opened from somewhere else). + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Neil Fortner + * Friday, December 17, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t +H5Pset_elink_file_cache_size(hid_t plist_id, unsigned efc_size) +{ + H5P_genplist_t *plist; /* Property list pointer */ + herr_t ret_value = SUCCEED; /* return value */ + + FUNC_ENTER_API(H5Pset_elink_file_cache_size, FAIL) + H5TRACE2("e", "iIu", plist_id, efc_size); + + /* Get the plist structure */ + if(NULL == (plist = H5P_object_verify(plist_id, H5P_FILE_ACCESS))) + HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID") + + /* Set value */ + if(H5P_set(plist, H5F_ACS_EFC_SIZE_NAME, &efc_size) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set elink file cache size") + +done: + FUNC_LEAVE_API(ret_value) +} /* end H5Pset_elink_file_cache_size() */ + + +/*------------------------------------------------------------------------- + * Function: H5Pget_elink_file_cache_size + * + * Purpose: Gets the number of files opened through external links + * from the file associated with this fapl to be held open + * in that file's external file cache. When the maximum + * number of files is reached, the least recently used file + * is closed (unless it is opened from somewhere else). + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Neil Fortner + * Friday, December 17, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t +H5Pget_elink_file_cache_size(hid_t plist_id, unsigned *efc_size) +{ + H5P_genplist_t *plist; /* Property list pointer */ + herr_t ret_value = SUCCEED; /* return value */ + + FUNC_ENTER_API(H5Pget_elink_file_cache_size, FAIL) + H5TRACE2("e", "i*Iu", plist_id, efc_size); + + /* Get the plist structure */ + if(NULL == (plist = H5P_object_verify(plist_id, H5P_FILE_ACCESS))) + HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID") + + /* Get value */ + if(efc_size) + if(H5P_get(plist, H5F_ACS_EFC_SIZE_NAME, efc_size) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get elink file cache size") + +done: + FUNC_LEAVE_API(ret_value) +} /* end H5Pget_elink_file_cache_size() */ + diff --git a/src/H5Ppublic.h b/src/H5Ppublic.h index 09eb796..6867b63 100644 --- a/src/H5Ppublic.h +++ b/src/H5Ppublic.h @@ -287,6 +287,8 @@ H5_DLL herr_t H5Pset_libver_bounds(hid_t plist_id, H5F_libver_t low, H5F_libver_t high); H5_DLL herr_t H5Pget_libver_bounds(hid_t plist_id, H5F_libver_t *low, H5F_libver_t *high); +H5_DLL herr_t H5Pset_elink_file_cache_size(hid_t plist_id, unsigned efc_size); +H5_DLL herr_t H5Pget_elink_file_cache_size(hid_t plist_id, unsigned *efc_size); /* Dataset creation property list (DCPL) routines */ H5_DLL herr_t H5Pset_layout(hid_t plist_id, H5D_layout_t layout); diff --git a/src/Makefile.am b/src/Makefile.am index dc0edb3..ee0a3f5 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,8 +53,8 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5system.c H5timer.c H5trace.c \ H5E.c H5Edeprec.c H5Eint.c \ H5EA.c H5EAcache.c H5EAdbg.c H5EAdblkpage.c H5EAdblock.c H5EAhdr.c \ H5EAiblock.c H5EAint.c H5EAsblock.c H5EAstat.c H5EAtest.c \ - H5F.c H5Faccum.c H5Fdbg.c H5Fdeprec.c H5Ffake.c H5Fio.c H5Fmount.c \ - H5Fmpi.c H5Fquery.c \ + H5F.c H5Faccum.c H5Fdbg.c H5Fdeprec.c H5Fefc.c H5Ffake.c H5Fio.c \ + H5Fmount.c H5Fmpi.c H5Fquery.c \ H5Fsfile.c H5Fsuper.c H5Fsuper_cache.c H5Ftest.c \ H5FA.c H5FAcache.c H5FAdbg.c H5FAdblock.c H5FAdblkpage.c H5FAhdr.c \ H5FAstat.c H5FAtest.c \ diff --git a/src/Makefile.in b/src/Makefile.in index 55e52ae..51003a0 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -106,19 +106,19 @@ am_libhdf5_la_OBJECTS = H5.lo H5checksum.lo H5dbg.lo H5system.lo \ H5Dtest.lo H5E.lo H5Edeprec.lo H5Eint.lo H5EA.lo H5EAcache.lo \ H5EAdbg.lo H5EAdblkpage.lo H5EAdblock.lo H5EAhdr.lo \ H5EAiblock.lo H5EAint.lo H5EAsblock.lo H5EAstat.lo H5EAtest.lo \ - H5F.lo H5Faccum.lo H5Fdbg.lo H5Fdeprec.lo H5Ffake.lo H5Fio.lo \ - H5Fmount.lo H5Fmpi.lo H5Fquery.lo H5Fsfile.lo H5Fsuper.lo \ - H5Fsuper_cache.lo H5Ftest.lo H5FA.lo H5FAcache.lo H5FAdbg.lo \ - H5FAdblock.lo H5FAdblkpage.lo H5FAhdr.lo H5FAstat.lo \ - H5FAtest.lo H5FD.lo H5FDcore.lo H5FDdirect.lo H5FDfamily.lo \ - H5FDint.lo H5FDlog.lo H5FDmpi.lo H5FDmpio.lo H5FDmpiposix.lo \ - H5FDmulti.lo H5FDsec2.lo H5FDspace.lo H5FDstdio.lo H5FL.lo \ - H5FO.lo H5FS.lo H5FScache.lo H5FSdbg.lo H5FSsection.lo \ - H5FSstat.lo H5FStest.lo H5G.lo H5Gbtree2.lo H5Gcache.lo \ - H5Gcompact.lo H5Gdense.lo H5Gdeprec.lo H5Gent.lo H5Gint.lo \ - H5Glink.lo H5Gloc.lo H5Gname.lo H5Gnode.lo H5Gobj.lo H5Goh.lo \ - H5Groot.lo H5Gstab.lo H5Gtest.lo H5Gtraverse.lo H5HF.lo \ - H5HFbtree2.lo H5HFcache.lo H5HFdbg.lo H5HFdblock.lo \ + H5F.lo H5Faccum.lo H5Fdbg.lo H5Fdeprec.lo H5Fefc.lo H5Ffake.lo \ + H5Fio.lo H5Fmount.lo H5Fmpi.lo H5Fquery.lo H5Fsfile.lo \ + H5Fsuper.lo H5Fsuper_cache.lo H5Ftest.lo H5FA.lo H5FAcache.lo \ + H5FAdbg.lo H5FAdblock.lo H5FAdblkpage.lo H5FAhdr.lo \ + H5FAstat.lo H5FAtest.lo H5FD.lo H5FDcore.lo H5FDdirect.lo \ + H5FDfamily.lo H5FDint.lo H5FDlog.lo H5FDmpi.lo H5FDmpio.lo \ + H5FDmpiposix.lo H5FDmulti.lo H5FDsec2.lo H5FDspace.lo \ + H5FDstdio.lo H5FL.lo H5FO.lo H5FS.lo H5FScache.lo H5FSdbg.lo \ + H5FSsection.lo H5FSstat.lo H5FStest.lo H5G.lo H5Gbtree2.lo \ + H5Gcache.lo H5Gcompact.lo H5Gdense.lo H5Gdeprec.lo H5Gent.lo \ + H5Gint.lo H5Glink.lo H5Gloc.lo H5Gname.lo H5Gnode.lo H5Gobj.lo \ + H5Goh.lo H5Groot.lo H5Gstab.lo H5Gtest.lo H5Gtraverse.lo \ + H5HF.lo H5HFbtree2.lo H5HFcache.lo H5HFdbg.lo H5HFdblock.lo \ H5HFdtable.lo H5HFhdr.lo H5HFhuge.lo H5HFiblock.lo H5HFiter.lo \ H5HFman.lo H5HFsection.lo H5HFspace.lo H5HFstat.lo H5HFtest.lo \ H5HFtiny.lo H5HG.lo H5HGcache.lo H5HGdbg.lo H5HL.lo \ @@ -486,8 +486,8 @@ libhdf5_la_SOURCES = H5.c H5checksum.c H5dbg.c H5system.c H5timer.c H5trace.c \ H5E.c H5Edeprec.c H5Eint.c \ H5EA.c H5EAcache.c H5EAdbg.c H5EAdblkpage.c H5EAdblock.c H5EAhdr.c \ H5EAiblock.c H5EAint.c H5EAsblock.c H5EAstat.c H5EAtest.c \ - H5F.c H5Faccum.c H5Fdbg.c H5Fdeprec.c H5Ffake.c H5Fio.c H5Fmount.c \ - H5Fmpi.c H5Fquery.c \ + H5F.c H5Faccum.c H5Fdbg.c H5Fdeprec.c H5Fefc.c H5Ffake.c H5Fio.c \ + H5Fmount.c H5Fmpi.c H5Fquery.c \ H5Fsfile.c H5Fsuper.c H5Fsuper_cache.c H5Ftest.c \ H5FA.c H5FAcache.c H5FAdbg.c H5FAdblock.c H5FAdblkpage.c H5FAhdr.c \ H5FAstat.c H5FAtest.c \ @@ -764,6 +764,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H5Faccum.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H5Fdbg.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H5Fdeprec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H5Fefc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H5Ffake.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H5Fio.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H5Fmount.Plo@am__quote@ |