1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * Copyright by The HDF Group.                                               *
3  * Copyright by the Board of Trustees of the University of Illinois.         *
4  * All rights reserved.                                                      *
5  *                                                                           *
6  * This file is part of HDF5.  The full HDF5 copyright notice, including     *
7  * terms governing use, modification, and redistribution, is contained in    *
8  * the COPYING file, which can be found at the root of the source code       *
9  * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
10  * If you do not have access to either file, you may request a copy from     *
11  * help@hdfgroup.org.                                                        *
12  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13 
14 /*-------------------------------------------------------------------------
15  *
16  * Created:             H5Defc.c
17  *                      December 13, 2010
18  *                      Neil Fortner <nfortne2@hdfgroup.org>
19  *
20  * Purpose:             External file caching routines - implements a
21  *                      cache of external files to minimize the number of
22  *                      file opens and closes.
23  *
24  *-------------------------------------------------------------------------
25  */
26 
27 #define H5F_PACKAGE             /*suppress error about including H5Fpkg   */
28 
29 
30 /* Packages needed by this file... */
31 #include "H5private.h"          /* Generic Functions                    */
32 #include "H5Eprivate.h"         /* Error handling                       */
33 #include "H5Fpkg.h"             /* File access                          */
34 #include "H5MMprivate.h"        /* Memory management                    */
35 #include "H5Pprivate.h"         /* Property lists                       */
36 
37 /* Special values for the "tag" field below */
38 #define H5F_EFC_TAG_DEFAULT     -1
39 #define H5F_EFC_TAG_LOCK        -2
40 #define H5F_EFC_TAG_CLOSE       -3
41 #define H5F_EFC_TAG_DONTCLOSE   -4
42 
43 /* Structure for each entry in a file's external file cache */
44 typedef struct H5F_efc_ent_t {
45     char                *name;          /* Name of the file */
46     H5F_t               *file;          /* File object */
47     struct H5F_efc_ent_t *LRU_next;     /* Next item in LRU list */
48     struct H5F_efc_ent_t *LRU_prev;     /* Previous item in LRU list */
49     unsigned            nopen;          /* Number of times this file is currently opened by an EFC client */
50 } H5F_efc_ent_t;
51 
52 /* Structure for a shared file struct's external file cache */
53 struct H5F_efc_t {
54     H5SL_t              *slist;         /* Skip list of cached external files */
55     H5F_efc_ent_t       *LRU_head;      /* Head of LRU list.  This is the least recently used file */
56     H5F_efc_ent_t       *LRU_tail;      /* Tail of LRU list.  This is the most recently used file */
57     unsigned            nfiles;         /* Size of the external file cache */
58     unsigned            max_nfiles;     /* Maximum size of the external file cache */
59     unsigned            nrefs;          /* Number of times this file appears in another file's EFC */
60     int                 tag;            /* Temporary variable used by H5F_efc_try_close() */
61     H5F_file_t          *tmp_next;      /* Next file in temporary list used by H5F_efc_try_close() */
62 };
63 
64 /* Private prototypes */
65 static herr_t H5F_efc_remove_ent(H5F_efc_t *efc, H5F_efc_ent_t *ent);
66 static void H5F_efc_try_close_tag1(H5F_file_t *sf, H5F_file_t **tail);
67 static void H5F_efc_try_close_tag2(H5F_file_t *sf, H5F_file_t **tail);
68 
69 /* Free lists */
70 H5FL_DEFINE_STATIC(H5F_efc_ent_t);
71 H5FL_DEFINE_STATIC(H5F_efc_t);
72 
73 
74 /*-------------------------------------------------------------------------
75  * Function:    H5F_efc_create
76  *
77  * Purpose:     Allocate and initialize a new external file cache object,
78  *              which can the be used to cache open external files.
79  *              the object must be freed with H5F_efc_destroy.
80  *
81  * Return:      Pointer to new external file cache object on success
82  *              NULL on failure
83  *
84  * Programmer:  Neil Fortner
85  *              Tuesday, December 14, 2010
86  *
87  *-------------------------------------------------------------------------
88  */
89 H5F_efc_t *
H5F_efc_create(unsigned max_nfiles)90 H5F_efc_create(unsigned max_nfiles)
91 {
92     H5F_efc_t   *efc = NULL;            /* EFC object */
93     H5F_efc_t   *ret_value;             /* Return value */
94 
95     FUNC_ENTER_NOAPI(NULL)
96 
97     /* Sanity checks */
98     HDassert(max_nfiles > 0);
99 
100     /* Allocate EFC struct */
101     if(NULL == (efc = H5FL_CALLOC(H5F_efc_t)))
102         HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed")
103 
104     /* Initialize maximum number of files */
105     efc->max_nfiles = max_nfiles;
106 
107     /* Initialize temporary ref count */
108     efc->tag = H5F_EFC_TAG_DEFAULT;
109 
110     /* Set the return value */
111     ret_value = efc;
112 
113 done:
114     if(ret_value == NULL && efc)
115         efc = H5FL_FREE(H5F_efc_t, efc);
116 
117     FUNC_LEAVE_NOAPI(ret_value)
118 } /* end H5F_efc_create() */
119 
120 
121 /*-------------------------------------------------------------------------
122  * Function:    H5F_efc_open
123  *
124  * Purpose:     Opens a file using the external file cache.  The target
125  *              file is added to the external file cache of the parent
126  *              if it is not already present.  If the target file is in
127  *              the parent's EFC, simply returns the target file.  When
128  *              the file object is no longer in use, it should be closed
129  *              with H5F_efc_close (will not actually close the file
130  *              until it is evicted from the EFC).
131  *
132  * Return:      Pointer to open file on success
133  *              NULL on failure
134  *
135  * Programmer:  Neil Fortner
136  *              Tuesday, December 14, 2010
137  *
138  *-------------------------------------------------------------------------
139  */
140 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)141 H5F_efc_open(H5F_t *parent, const char *name, unsigned flags, hid_t fcpl_id,
142     hid_t fapl_id, hid_t dxpl_id)
143 {
144     H5F_efc_t   *efc = NULL;    /* External file cache for parent file */
145     H5F_efc_ent_t *ent = NULL;  /* Entry for target file in efc */
146     hbool_t     open_file = FALSE; /* Whether ent->file needs to be closed in case of error */
147     H5F_t       *ret_value = NULL; /* Return value */
148 
149     FUNC_ENTER_NOAPI_NOINIT
150 
151     /* Sanity checks */
152     HDassert(parent);
153     HDassert(parent->shared);
154     HDassert(name);
155 
156     /* Get external file cache */
157     efc = parent->shared->efc;
158 
159     /* Check if the EFC exists.  If it does not, just call H5F_open().  We
160      * support this so clients do not have to make 2 different calls depending
161      * on the state of the efc. */
162     if(!efc) {
163         if(NULL == (ret_value = H5F_open(name, flags, fcpl_id, fapl_id,
164                                          dxpl_id)))
165             HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "can't open file")
166 
167         /* Increment the number of open objects to prevent the file from being
168          * closed out from under us - "simulate" having an open file id.  Note
169          * that this behaviour replaces the calls to H5F_incr_nopen_objs() and
170          * H5F_decr_nopen_objs() in H5L_extern_traverse(). */
171         ret_value->nopen_objs++;
172 
173         HGOTO_DONE(ret_value)
174     } /* end if */
175 
176     /* Search the skip list for name if the skip list exists, create the skip
177      * list otherwise */
178     if(efc->slist) {
179         if(efc->nfiles > 0)
180             ent = (H5F_efc_ent_t *)H5SL_search(efc->slist, name);
181     } /* end if */
182     else {
183         HDassert(efc->nfiles == 0);
184         if(NULL == (efc->slist = H5SL_create(H5SL_TYPE_STR, NULL)))
185             HGOTO_ERROR(H5E_FILE, H5E_CANTCREATE, NULL, "can't create skip list")
186     } /* end else */
187 
188     /* If we found the file update the LRU list and return the cached file,
189      * otherwise open the file and cache it */
190     if(ent) {
191         HDassert(efc->LRU_head);
192         HDassert(efc->LRU_tail);
193 
194         /* Move ent to the head of the LRU list, if it is not already there */
195         if(ent->LRU_prev) {
196             HDassert(efc->LRU_head != ent);
197 
198             /* Remove from current position.  Note that once we touch the LRU
199              * list we cannot revert to the previous state.  Make sure there can
200              * be no errors between when we first touch the LRU list and when
201              * the cache is in a consistent state! */
202             if(ent->LRU_next)
203                 ent->LRU_next->LRU_prev = ent->LRU_prev;
204             else {
205                 HDassert(efc->LRU_tail == ent);
206                 efc->LRU_tail = ent->LRU_prev;
207             } /* end else */
208             ent->LRU_prev->LRU_next = ent->LRU_next;
209 
210             /* Add to head of LRU list */
211             ent->LRU_next = efc->LRU_head;
212             ent->LRU_next->LRU_prev = ent;
213             ent->LRU_prev = NULL;
214             efc->LRU_head = ent;
215         } /* end if */
216 
217         /* Mark the file as open */
218         ent->nopen++;
219     } /* end if */
220     else {
221         /* Check if we need to evict something */
222         if(efc->nfiles == efc->max_nfiles) {
223             /* Search for an unopened file from the tail */
224             for(ent = efc->LRU_tail; ent && ent->nopen; ent = ent->LRU_prev);
225 
226             /* Evict the file if found, otherwise just open the target file and
227              * do not add it to cache */
228             if(ent) {
229                 if(H5F_efc_remove_ent(efc, ent) < 0)
230                     HGOTO_ERROR(H5E_FILE, H5E_CANTREMOVE, NULL, "can't remove entry from external file cache")
231 
232                 /* Do not free ent, we will recycle it below */
233             } /* end if */
234             else {
235                 /* Cannot cache file, just open file and return */
236                 if(NULL == (ret_value = H5F_open(name, flags, fcpl_id, fapl_id,
237                                                  dxpl_id)))
238                     HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "can't open file")
239 
240                 /* Increment the number of open objects to prevent the file from
241                  * being closed out from under us - "simulate" having an open
242                  * file id */
243                 ret_value->nopen_objs++;
244 
245                 HGOTO_DONE(ret_value)
246             } /* end else */
247         } /* end if */
248         else
249             /* Allocate new entry */
250             if(NULL == (ent = H5FL_MALLOC(H5F_efc_ent_t)))
251                 HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed")
252 
253         /* Build new entry */
254         if(NULL == (ent->name = H5MM_strdup(name)))
255             HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed")
256 
257         /* Open the file */
258         if(NULL == (ent->file = H5F_open(name, flags, fcpl_id, fapl_id,
259                                          dxpl_id)))
260             HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "can't open file")
261         open_file = TRUE;
262 
263         /* Increment the number of open objects to prevent the file from being
264          * closed out from under us - "simulate" having an open file id */
265         ent->file->nopen_objs++;
266 
267         /* Add the file to the cache */
268         /* Skip list */
269         if(H5SL_insert(efc->slist, ent, ent->name) < 0)
270             HGOTO_ERROR(H5E_FILE, H5E_CANTINSERT, NULL, "can't insert entry into skip list")
271 
272         /* Add to head of LRU list and update tail if necessary */
273         ent->LRU_next = efc->LRU_head;
274         if(ent->LRU_next)
275             ent->LRU_next->LRU_prev = ent;
276         ent->LRU_prev = NULL;
277         efc->LRU_head = ent;
278         if(!efc->LRU_tail) {
279             HDassert(!ent->LRU_next);
280             efc->LRU_tail = ent;
281         } /* end if */
282 
283         /* Mark the file as open */
284         ent->nopen = 1;
285 
286         /* Update nfiles and nrefs */
287         efc->nfiles++;
288         if(ent->file->shared->efc)
289             ent->file->shared->efc->nrefs++;
290     } /* end else */
291 
292     HDassert(ent);
293     HDassert(ent->file);
294     HDassert(ent->name);
295     HDassert(ent->nopen);
296 
297     /* Set the return value */
298     ret_value = ent->file;
299 
300 done:
301     if(!ret_value)
302         if(ent) {
303             if(open_file) {
304                 ent->file->nopen_objs--;
305                 if(H5F_try_close(ent->file) < 0)
306                     HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, NULL, "can't close external file")
307             } /* end if */
308             ent->name = (char *)H5MM_xfree(ent->name);
309             ent = H5FL_FREE(H5F_efc_ent_t, ent);
310         } /* end if */
311 
312     FUNC_LEAVE_NOAPI(ret_value)
313 } /* end H5F_efc_open() */
314 
315 
316 /*-------------------------------------------------------------------------
317  * Function:    H5F_efc_close
318  *
319  * Purpose:     Closes (unlocks) a file opened using the external file
320  *              cache.  The target file is not immediately closed unless
321  *              there is no external file cache for the parent file.
322  *
323  * Return:      Non-negative on success
324  *              Negative on failure
325  *
326  * Programmer:  Neil Fortner
327  *              Wednesday, December 15, 2010
328  *
329  *-------------------------------------------------------------------------
330  */
331 herr_t
H5F_efc_close(H5F_t * parent,H5F_t * file)332 H5F_efc_close(H5F_t *parent, H5F_t *file)
333 {
334     H5F_efc_t   *efc = NULL;    /* External file cache for parent file */
335     H5F_efc_ent_t *ent = NULL;  /* Entry for target file in efc */
336     herr_t      ret_value = SUCCEED; /* Return value */
337 
338     FUNC_ENTER_NOAPI_NOINIT
339 
340     /* Sanity checks */
341     HDassert(parent);
342     HDassert(parent->shared);
343     HDassert(file);
344     HDassert(file->shared);
345 
346     /* Get external file cache */
347     efc = parent->shared->efc;
348 
349     /* Check if the EFC exists.  If it does not, just call H5F_try_close().  We
350      * support this so clients do not have to make 2 different calls depending
351      * on the state of the efc. */
352     if(!efc) {
353         file->nopen_objs--;
354         if(H5F_try_close(file) < 0)
355             HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "can't close external file")
356 
357         HGOTO_DONE(SUCCEED)
358     } /* end if */
359 
360     /* Scan the parent's LRU list from the head to file file.  We do this
361      * instead of a skip list lookup because the file will almost always be at
362      * the head.  In the unlikely case that the file is not found, just call
363      * H5F_try_close().  This could happen if the EFC was full of open files
364      * when the file was opened. */
365     for(ent = efc->LRU_head; ent && ent->file != file; ent = ent->LRU_next);
366     if(!ent) {
367         file->nopen_objs--;
368         if(H5F_try_close(file) < 0)
369             HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "can't close external file")
370     } /* end if */
371     else
372         /* Reduce the open count on this entry */
373         ent->nopen--;
374 
375 done:
376     FUNC_LEAVE_NOAPI(ret_value)
377 } /* end H5F_efc_close() */
378 
379 
380 /*-------------------------------------------------------------------------
381  * Function:    H5F_efc_max_nfiles
382  *
383  * Purpose:     Returns the maximum number of files in the provided
384  *              external file cache.
385  *
386  * Return:      Maximum number of files (never fails)
387  *
388  * Programmer:  Neil Fortner
389  *              Wednesday, December 15, 2010
390  *
391  *-------------------------------------------------------------------------
392  */
393 unsigned
H5F_efc_max_nfiles(H5F_efc_t * efc)394 H5F_efc_max_nfiles(H5F_efc_t *efc)
395 {
396     FUNC_ENTER_NOAPI_NOINIT_NOERR
397 
398     HDassert(efc);
399     HDassert(efc->max_nfiles > 0);
400 
401     FUNC_LEAVE_NOAPI(efc->max_nfiles)
402 } /* end H5F_efc_max_nfiles */
403 
404 
405 /*-------------------------------------------------------------------------
406  * Function:    H5F_efc_release
407  *
408  * Purpose:     Releases the external file cache, potentially closing any
409  *              cached files unless they are held open from somewhere
410  *              else (or are currently opened by a client).
411  *
412  * Return:      Non-negative on success
413  *              Negative on failure
414  *
415  * Programmer:  Neil Fortner
416  *              Wednesday, December 15, 2010
417  *
418  *-------------------------------------------------------------------------
419  */
420 herr_t
H5F_efc_release(H5F_efc_t * efc)421 H5F_efc_release(H5F_efc_t *efc)
422 {
423     H5F_efc_ent_t *ent = NULL;          /* EFC entry */
424     H5F_efc_ent_t *prev_ent = NULL;     /* Previous EFC entry */
425     herr_t      ret_value = SUCCEED;    /* Return value */
426 
427     FUNC_ENTER_NOAPI_NOINIT
428 
429     /* Sanity checks */
430     HDassert(efc);
431 
432     /* Lock the EFC to prevent manipulation of the EFC wile we are releasing it.
433      * The EFC should never be locked when we enter this function because that
434      * would require a cycle, a cycle would necessarily invoke
435      * H5F_efc_try_close(), and that function checks the status of the lock
436      * before calling this one. */
437     HDassert((efc->tag == H5F_EFC_TAG_DEFAULT)
438             || (efc->tag == H5F_EFC_TAG_CLOSE));
439     efc->tag = H5F_EFC_TAG_LOCK;
440 
441     /* Walk down the LRU list, releasing any files that are not opened by an EFC
442      * client */
443     ent = efc->LRU_head;
444     while(ent)
445         if(!ent->nopen) {
446             if(H5F_efc_remove_ent(efc, ent) < 0)
447                 HGOTO_ERROR(H5E_FILE, H5E_CANTREMOVE, FAIL, "can't remove entry from external file cache")
448 
449             /* Free the entry and move to next entry in LRU list */
450             prev_ent = ent;
451             ent = ent->LRU_next;
452             prev_ent = H5FL_FREE(H5F_efc_ent_t, prev_ent);
453         } /* end if */
454         else
455             /* Can't release file because it's open; just advance the pointer */
456             ent = ent->LRU_next;
457 
458     /* Reset tag.  No need to reset to CLOSE if that was the original tag, as in
459      * that case the file must be getting closed anyways. */
460     efc->tag = H5F_EFC_TAG_DEFAULT;
461 
462 done:
463     FUNC_LEAVE_NOAPI(ret_value);
464 } /* end H5F_efc_release() */
465 
466 
467 /*-------------------------------------------------------------------------
468  * Function:    H5F_efc_destroy
469  *
470  * Purpose:     Frees an external file cache object, releasing it first
471  *              if necessary.  If it cannot be fully released, for example
472  *              if there are open files, returns an error.
473  *
474  * Return:      Non-negative on success
475  *              Negative on failure
476  *
477  * Programmer:  Neil Fortner
478  *              Wednesday, December 15, 2010
479  *
480  *-------------------------------------------------------------------------
481  */
482 herr_t
H5F_efc_destroy(H5F_efc_t * efc)483 H5F_efc_destroy(H5F_efc_t *efc)
484 {
485     herr_t      ret_value = SUCCEED;    /* Return value */
486 
487     FUNC_ENTER_NOAPI_NOINIT
488 
489     /* Sanity checks */
490     HDassert(efc);
491 
492     if(efc->nfiles > 0) {
493         /* Release (clear) the efc */
494         if(H5F_efc_release(efc) < 0)
495             HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "can't release external file cache")
496 
497         /* If there are still cached files, return an error */
498         if(efc->nfiles > 0)
499             HGOTO_ERROR(H5E_FILE, H5E_CANTFREE, FAIL, "can't destroy EFC after incomplete release")
500     } /* end if */
501 
502     HDassert(efc->nfiles == 0);
503     HDassert(efc->LRU_head == NULL);
504     HDassert(efc->LRU_tail == NULL);
505 
506     /* Close skip list */
507     if(efc->slist)
508         if(H5SL_close(efc->slist) < 0)
509             HGOTO_ERROR(H5E_FILE, H5E_CANTFREE, FAIL, "can't close skip list")
510 
511     /* Free EFC object */
512     (void)H5FL_FREE(H5F_efc_t, efc);
513 
514 done:
515     FUNC_LEAVE_NOAPI(ret_value);
516 } /* end H5F_efc_destroy() */
517 
518 
519 /*-------------------------------------------------------------------------
520  * Function:    H5F_efc_remove_ent
521  *
522  * Purpose:     Removes the specified entry from the specified EFC,
523  *              closing the file if requested.  Does not free the entry.
524  *
525  * Return:      Non-negative on success
526  *              Negative on failure
527  *
528  * Programmer:  Neil Fortner
529  *              Wednesday, December 15, 2010
530  *
531  *-------------------------------------------------------------------------
532  */
533 static herr_t
H5F_efc_remove_ent(H5F_efc_t * efc,H5F_efc_ent_t * ent)534 H5F_efc_remove_ent(H5F_efc_t *efc, H5F_efc_ent_t *ent)
535 {
536     herr_t      ret_value = SUCCEED;    /* Return value */
537 
538     FUNC_ENTER_NOAPI_NOINIT
539 
540     /* Sanity checks */
541     HDassert(efc);
542     HDassert(efc->slist);
543     HDassert(ent);
544 
545     /* Remove from skip list */
546     if(ent != H5SL_remove(efc->slist, ent->name))
547         HGOTO_ERROR(H5E_FILE, H5E_CANTDELETE, FAIL, "can't delete entry from skip list")
548 
549     /* Remove from LRU list */
550     if(ent->LRU_next)
551         ent->LRU_next->LRU_prev = ent->LRU_prev;
552     else {
553         HDassert(efc->LRU_tail == ent);
554         efc->LRU_tail = ent->LRU_prev;
555     } /* end else */
556     if(ent->LRU_prev)
557         ent->LRU_prev->LRU_next = ent->LRU_next;
558     else {
559         HDassert(efc->LRU_head == ent);
560         efc->LRU_head = ent->LRU_next;
561     } /* end else */
562 
563     /* Update nfiles and nrefs */
564     efc->nfiles--;
565     if(ent->file->shared->efc)
566         ent->file->shared->efc->nrefs--;
567 
568     /* Free the name */
569     ent->name = (char *)H5MM_xfree(ent->name);
570 
571     /* Close the file.  Note that since H5F_t structs returned from H5F_open()
572      * are *always* unique, there is no need to reference count this struct.
573      * However we must still manipulate the nopen_objs field to prevent the file
574      * from being closed out from under us. */
575     ent->file->nopen_objs--;
576     if(H5F_try_close(ent->file) < 0)
577         HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "can't close external file")
578     ent->file = NULL;
579 
580 done:
581     FUNC_LEAVE_NOAPI(ret_value)
582 } /* end H5F_efc_remove_ent() */
583 
584 
585 /*-------------------------------------------------------------------------
586  * Function:    H5F_efc_try_close_tag1
587  *
588  * Purpose:     Recursively traverse the EFC tree, keeping a temporary
589  *              reference count on each file that assumes all reachable
590  *              files will eventually be closed.
591  *
592  * Return:      void (never fails)
593  *
594  * Programmer:  Neil Fortner
595  *              Monday, January 10, 2011
596  *
597  *-------------------------------------------------------------------------
598  */
599 static void
H5F_efc_try_close_tag1(H5F_file_t * sf,H5F_file_t ** tail)600 H5F_efc_try_close_tag1(H5F_file_t *sf, H5F_file_t **tail)
601 {
602     H5F_efc_ent_t       *ent = NULL;    /* EFC entry */
603     H5F_file_t          *esf;           /* Convenience pointer to ent->file->shared */
604 
605     FUNC_ENTER_NOAPI_NOINIT_NOERR
606 
607     /* Sanity checks */
608     HDassert(sf);
609     HDassert(sf->efc);
610     HDassert((sf->efc->tag > 0) || (sf->nrefs == sf->efc->nrefs));
611     HDassert(sf->efc->tag != H5F_EFC_TAG_LOCK);
612     HDassert(tail);
613     HDassert(*tail);
614 
615     /* Recurse into this file's cached files */
616     for(ent = sf->efc->LRU_head; ent; ent = ent->LRU_next) {
617         esf = ent->file->shared;
618 
619         if(esf->efc) {
620             /* If tag were 0, that would mean there are more actual references
621              * than are counted by nrefs */
622             HDassert(esf->efc->tag != 0);
623 
624             /* If tag has been set, we have already visited this file so just
625              * decrement tag and continue */
626             if(esf->efc->tag > 0)
627                 esf->efc->tag--;
628             /* If there are references that are not from an EFC, it will never
629              * be possible to close the file.  Just continue.  Also continue if
630              * the EFC is locked or the file is open (through the EFC).  Note
631              * that the reference counts will never match for the root file, but
632              * that's ok because the root file will always have a tag and enter
633              * the branch above. */
634             else if((esf->nrefs == esf->efc->nrefs)
635                     && (esf->efc->tag != H5F_EFC_TAG_LOCK) && !(ent->nopen)) {
636                 /* If we get here, this file's "tmp_next" pointer must be NULL
637                  */
638                 HDassert(esf->efc->tmp_next == NULL);
639 
640                 /* If nrefs > 1, Add this file to the list of files with nrefs >
641                  * 1 and initialize tag to the number of references (except this
642                  * one) */
643                 if(esf->nrefs > 1) {
644                     (*tail)->efc->tmp_next = esf;
645                     *tail = esf;
646                     esf->efc->tag = (int)esf->nrefs - 1;
647                 } /* end if */
648 
649                 /* Recurse into the entry */
650                 H5F_efc_try_close_tag1(ent->file->shared, tail);
651             } /* end if */
652         } /* end if */
653     } /* end for */
654 
655     FUNC_LEAVE_NOAPI_VOID
656 } /* end H5F_efc_try_close_tag1() */
657 
658 
659 /*-------------------------------------------------------------------------
660  * Function:    H5F_efc_try_close_tag2
661  *
662  * Purpose:     Recuresively mark all files reachable through this one as
663  *              uncloseable, and add newly uncloseable files to the tail
664  *              of the provided linked list.
665  *
666  * Return:      void (never fails)
667  *
668  * Programmer:  Neil Fortner
669  *              Monday, January 10, 2011
670  *
671  *-------------------------------------------------------------------------
672  */
673 static void
H5F_efc_try_close_tag2(H5F_file_t * sf,H5F_file_t ** tail)674 H5F_efc_try_close_tag2(H5F_file_t *sf, H5F_file_t **tail)
675 {
676     H5F_efc_ent_t       *ent = NULL;    /* EFC entry */
677     H5F_file_t          *esf;           /* Convenience pointer to ent->file->shared */
678 
679     FUNC_ENTER_NOAPI_NOINIT_NOERR
680 
681     /* Sanity checks */
682     HDassert(sf);
683     HDassert(sf->efc);
684 
685     /* Recurse into this file's cached files */
686     for(ent = sf->efc->LRU_head; ent; ent = ent->LRU_next) {
687         esf = ent->file->shared;
688 
689         /* Only recurse if the file is tagged CLOSE or DEFAULT.  If it is tagged
690          * DONTCLOSE, we have already visited this file *or* it will be the
691          * start point of another iteration.  No files should be tagged with a
692          * nonegative value at this point.  If it is tagged as DEFAULT, we must
693          * apply the same conditions as in cb1 above for recursion in order to
694          * make sure  we do not go off into somewhere cb1 didn't touch.  The
695          * root file should never be tagged DEFAULT here, so the reference check
696          * is still appropriate. */
697         if((esf->efc) && ((esf->efc->tag == H5F_EFC_TAG_CLOSE)
698                 || ((esf->efc->tag == H5F_EFC_TAG_DEFAULT)
699                 && (esf->nrefs == esf->efc->nrefs) && !(ent->nopen)))) {
700             /* tag should always be CLOSE is nrefs > 1 or DEFAULT if nrefs == 1
701              * here */
702             HDassert(((esf->nrefs > 1)
703                     && ((esf->efc->tag == H5F_EFC_TAG_CLOSE)))
704                     || ((esf->nrefs == 1)
705                     && (esf->efc->tag == H5F_EFC_TAG_DEFAULT)));
706 
707             /* If tag is set to DONTCLOSE, we have already visited this file
708              * *or* it will be the start point of another iteration so just
709              * continue */
710             if(esf->efc->tag != H5F_EFC_TAG_DONTCLOSE) {
711                 /* If tag is CLOSE, set to DONTCLOSE and add to the list of
712                  * uncloseable files. */
713                 if(esf->efc->tag == H5F_EFC_TAG_CLOSE) {
714                     esf->efc->tag = H5F_EFC_TAG_DONTCLOSE;
715                     esf->efc->tmp_next = NULL;
716                     (*tail)->efc->tmp_next = esf;
717                     *tail = esf;
718                 } /* end if */
719 
720                 /* Recurse into the entry */
721                 H5F_efc_try_close_tag2(esf, tail);
722             } /* end if */
723         } /* end if */
724     } /* end for */
725 
726     FUNC_LEAVE_NOAPI_VOID
727 } /* end H5F_efc_try_close_tag2() */
728 
729 
730 /*-------------------------------------------------------------------------
731  * Function:    H5F_efc_try_close
732  *
733  * Purpose:     Attempts to close the provided (shared) file by checking
734  *              to see if the releasing the EFC would cause its reference
735  *              count to drop to 0.  Necessary to handle the case where
736  *              chained EFCs form a cycle.  Note that this function does
737  *              not actually close the file (though it closes all children
738  *              as appropriate), as that is left up to the calling
739  *              function H5F_try_close().
740  *
741  *              Because H5F_try_close() has no way of telling if it is
742  *              called recursively from within this function, this
743  *              function serves as both the root of iteration and the
744  *              "callback" for the final pass (the one where the files are
745  *              actually closed).  The code for the callback case is at
746  *              the top of this function; luckily it only consists of a
747  *              (possible) call to H5F_efc_release().
748  *
749  *              The algorithm basically consists of 3 passes over the EFC
750  *              tree.  The first pass assumes that every reachable file is
751  *              closed, and keeps track of what the final reference count
752  *              would be for every reachable file.  The files are then
753  *              tagged as either closeable or uncloseable based on whether
754  *              this reference count drops to 0.
755  *
756  *              The second pass initiates a traversal from each file
757  *              marked as uncloseable in the first pass, and marks every
758  *              file reachable from the initial uncloseable file as
759  *              uncloseable.  This eliminates files that were marked as
760  *              closeable only because the first pass assumed that an
761  *              uncloseable file would be closed.
762  *
763  *              The final pass exploits the H5F_efc_release()->
764  *              H5F_efc_remove_ent()->H5F_try_close()->H5F_efc_try_close()
765  *              calling chain to recursively close the tree, but only the
766  *              files that are still marked as closeable.  All files
767  *              marked as closeable have their EFCs released, and will
768  *              eventually be closed when their last parent EFC is
769  *              released (the last part is guaranteed to be true by the
770  *              first 2 passes).
771  *
772  * Return:      Non-negative on success
773  *              Negative on failure
774  *
775  * Programmer:  Neil Fortner
776  *              Thursday, January 6, 2011
777  *
778  *-------------------------------------------------------------------------
779  */
780 herr_t
H5F_efc_try_close(H5F_t * f)781 H5F_efc_try_close(H5F_t *f)
782 {
783     H5F_file_t  *tail;                  /* Tail of linked list of found files.  Head will be f->shared. */
784     H5F_file_t  *uncloseable_head = NULL; /* Head of linked list of files found to be uncloseable by the first pass */
785     H5F_file_t  *uncloseable_tail = NULL; /* Tail of linked list of files found to be uncloseable by the first pass */
786     H5F_file_t  *sf;                    /* Temporary file pointer */
787     H5F_file_t  *next;                  /* Temporary file pointer */
788     herr_t      ret_value = SUCCEED;    /* Return value */
789 
790     FUNC_ENTER_NOAPI_NOINIT
791 
792     /* Sanity checks */
793     HDassert(f);
794     HDassert(f->shared);
795     HDassert(f->shared->efc);
796     HDassert(f->shared->nrefs > f->shared->efc->nrefs);
797     HDassert(f->shared->nrefs > 1);
798     HDassert(f->shared->efc->tag < 0);
799 
800     if(f->shared->efc->tag == H5F_EFC_TAG_CLOSE) {
801         /* We must have reentered this function, and we should close this file.
802          * In actuality, we just release the EFC, the recursion should
803          * eventually reduce this file's reference count to 1 (though possibly
804          * not from this call to H5F_efc_release()). */
805         if(H5F_efc_release(f->shared->efc) < 0)
806             HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "can't release external file cache")
807 
808         /* If we marked the file as closeable, there must be no open files in
809          * its EFC.  This is because, in order to close an open child file, the
810          * client must keep a copy of the parent file open.  The algorithm
811          * detect that the parent file is open (directly or through an EFC) and
812          * refuse to close it.  Verify that all files were released from this
813          * EFC (i.e. none were open). */
814         HDassert(f->shared->efc->nfiles == 0);
815 
816         HGOTO_DONE(SUCCEED)
817     } /* end if */
818 
819     /* Conditions where we should not do anything and just return immediately */
820     /* If there are references that are not from an EFC or f, it will never
821      * be possible to close the file.  Just return.  Note that this holds true
822      * for the case that this file is being closed through H5F_efc_release()
823      * because that function (through H5F_efc_remove_ent()) decrements the EFC
824      * reference count before it calls H5F_try_close(). This may occur if this
825      * function is reentered. */
826     /* If the tag is H5F_EFC_TAG_DONTCLOSE, then we have definitely reentered
827      * this function, and this file has been marked as uncloseable, so we should
828      * not close/release it */
829     /* If nfiles is 0, then there is nothing to do.  Just return.  This may also
830      * occur on reentry (for example if this file was previously released). */
831     if((f->shared->nrefs != f->shared->efc->nrefs + 1)
832             || (f->shared->efc->tag == H5F_EFC_TAG_DONTCLOSE)
833             || (f->shared->efc->nfiles == 0))
834         /* We must have reentered this function, and we should not close this
835          * file.  Just return. */
836         HGOTO_DONE(SUCCEED)
837 
838     /* If the file EFC were locked, that should always mean that there exists
839      * a reference to this file that is not in an EFC (it may have just been
840      * removed from an EFC), and should have been caught by the above check */
841     /* If we get here then we must be beginning a new run.  Make sure that the
842      * temporary variables in f->shared->efc are at the default value */
843     HDassert(f->shared->efc->tag == H5F_EFC_TAG_DEFAULT);
844     HDassert(f->shared->efc->tmp_next == NULL);
845 
846     /* Set up linked list for traversal into EFC tree.  f->shared is guaranteed
847      * to always be at the head. */
848     tail = f->shared;
849 
850     /* Set up temporary reference count on root file */
851     f->shared->efc->tag = (int)f->shared->efc->nrefs;
852 
853     /* First Pass: simulate closing all files reachable from this one, use "tag"
854      * field to keep track of final reference count for each file (including
855      * this one).  Keep list of files with starting reference count > 1 (head is
856      * f->shared). */
857     H5F_efc_try_close_tag1(f->shared, &tail);
858 
859     /* Check if f->shared->efc->tag dropped to 0.  If it did not,
860      * we cannot close anything.  Just reset temporary values and return. */
861     if(f->shared->efc->tag > 0) {
862         sf = f->shared;
863         while(sf) {
864             next = sf->efc->tmp_next;
865             sf->efc->tag = H5F_EFC_TAG_DEFAULT;
866             sf->efc->tmp_next = NULL;
867             sf = next;
868         } /* end while */
869         HGOTO_DONE(SUCCEED)
870     } /* end if */
871 
872     /* Run through the linked list , separating into two lists, one with tag ==
873      * 0 and one with tag > 0.  Mark them as either H5F_EFC_TAG_CLOSE or
874      * H5F_EFC_TAG_DONTCLOSE as appropriate. */
875     sf = f->shared;
876     tail = NULL;
877     while(sf) {
878         HDassert(sf->efc->tag >= 0);
879         next = sf->efc->tmp_next;
880         if(sf->efc->tag > 0) {
881             /* Remove from main list */
882             HDassert(tail);
883             tail->efc->tmp_next = sf->efc->tmp_next;
884             sf->efc->tmp_next = NULL;
885 
886             /* Add to uncloseable list */
887             if(!uncloseable_head)
888                 uncloseable_head = sf;
889             else
890                 uncloseable_tail->efc->tmp_next = sf;
891             uncloseable_tail = sf;
892 
893             /* Mark as uncloseable */
894             sf->efc->tag = H5F_EFC_TAG_DONTCLOSE;
895         } /* end if */
896         else {
897             sf->efc->tag = H5F_EFC_TAG_CLOSE;
898             tail = sf;
899         } /* end else */
900         sf = next;
901     } /* end while */
902 
903     /* Second pass: Determine which of the reachable files found in pass 1
904      * cannot be closed by releasing the root file's EFC.  Run through the
905      * uncloseable list, for each item traverse the files reachable through the
906      * EFC, mark the file as uncloseable, and add it to the list of uncloseable
907      * files (for cleanup).  Use "tail" to store the original uncloseable tail
908      * so we know when to stop.  We do not need to keep track of the closeable
909      * list any more. */
910     sf = uncloseable_head;
911     if(sf) {
912         tail = uncloseable_tail;
913         HDassert(tail);
914         while(sf != tail->efc->tmp_next) {
915             H5F_efc_try_close_tag2(sf, &uncloseable_tail);
916             sf = sf->efc->tmp_next;
917         } /* end while */
918     } /* end if */
919 
920     /* If the root file's tag is still H5F_EFC_TAG_CLOSE, release its EFC.  This
921      * should start the recursive release that should close all closeable files.
922      * Also, see the top of this function. */
923     if(f->shared->efc->tag == H5F_EFC_TAG_CLOSE) {
924         if(H5F_efc_release(f->shared->efc) < 0)
925             HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "can't release external file cache")
926 
927         /* Make sure the file's reference count is now 1 and will be closed by
928          * H5F_dest(). */
929         HDassert(f->shared->nrefs == 1);
930     } /* end if */
931 
932     /* Clean up uncloseable files (reset tag and tmp_next).  All closeable files
933      * should have been closed, and therefore do not need to be cleaned up. */
934     if(uncloseable_head) {
935         sf = uncloseable_head;
936         while(sf) {
937             next = sf->efc->tmp_next;
938             HDassert(sf->efc->tag == H5F_EFC_TAG_DONTCLOSE);
939             sf->efc->tag = H5F_EFC_TAG_DEFAULT;
940             sf->efc->tmp_next = NULL;
941             sf = next;
942         } /* end while */
943     } /* end if */
944 
945 done:
946     FUNC_LEAVE_NOAPI(ret_value)
947 } /* end H5F_efc_try_close() */
948 
949