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