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