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