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