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