herr_t
ss_file_close(ss_file_t *file /* The file to be closed */
)
{
SS_ENTER(ss_file_close, herr_t);
ss_gfile_t *gfile=NULL; /* GFile array entry for the file owning the FILE object */
ss_table_t *table=NULL; /* A persistent object table */
ss_scope_t topscope=SS_SCOPE_NULL; /* The top scope of FILE */
ss_scope_t *reg=NULL; /* Object registry entry */
int nopen=0; /* Number of File objects explicitly open in this file */
size_t gfile_idx, nreg, reg_idx;
htri_t is_same;
/* File must be explicitly open in order to be closed */
if (ss_file_isopen(file, NULL)<=0) SS_ERROR_FMT(PERM, ("FILE is not an open file"));
if (NULL==(gfile = SS_GFILE_LINK(file))) SS_ERROR(NOTFOUND);
if (gfile->cur_open<=0) SS_ERROR_FMT(PERM, ("FILE is not explicitly open"));
if (NULL==ss_file_topscope(file, &topscope)) SS_ERROR(FAILED);
/* Flush the file before we even try anything else. We can skip this for a transient file. */
if (!ss_file_istransient(file)) {
if (ss_file_synchronize(file, NULL)<0) SS_ERROR(FAILED);
if (ss_file_flush(file, NULL)<0) SS_ERROR(FAILED);
}
/* Look some things up before we start destroying data structures */
if (NULL==(gfile=SS_GFILE_LINK(&topscope))) SS_ERROR(FAILED);
/* Close the file for real if this is the last file that explicitly references the underlying HDF5 file. Do not
* decrement the gfile->cur_open yet because some functions below will need to know that the file isn't completely
* closed yet (e.g., ss_pers_deref()). */
if (1==gfile->cur_open) {
/* Find all top-level scopes that use this as a registry and remove that association */
for (gfile_idx=0; NULL!=(gfile=SS_GFILE_IDX(gfile_idx)); gfile_idx++) {
if (gfile->cur_open>0) {
nreg = gfile->reg_nused;
reg = gfile->reg;
reg_idx = 0;
while (reg_idx++<nreg) {
if ((is_same=SS_PERS_EQ(&topscope, reg))<0) SS_ERROR(FAILED);
if (is_same) {
--nreg;
memmove(reg, reg+1, nreg*sizeof(*reg));
reg[nreg] = SS_SCOPE_NULL;
gfile->reg_nused -= 1;
} else {
reg++;
}
}
}
}
/* Remove all this file's registries */
gfile = SS_GFILE_LINK(&topscope);
if (gfile->reg_nused) {
gfile->reg_nused = 0;
gfile->reg_nalloc = 0;
gfile->reg = SS_FREE(gfile->reg);
}
/* Close all open scopes in the closing file. This removes the objects from memory (some of which may occupy a
* substantial amount of memory) but leaves the relatively small indirect index mapping information so that any
* persistent object that still points into the closing file will be able to convert indirect object links into direct
* object links if necessary. It would be nice to be able to also close any File objects that might have been
* explicitly opened, but alas, our current collectivity might not match that by which the contained File object was
* opened and hence must be closed; but we can warn about that with some extra work. */
if (NULL==(table=ss_scope_table(&topscope, SS_MAGIC(ss_scope_t), NULL))) SS_ERROR(FAILED);
if (ss_table_scan(table, &topscope, 0, ss_file_close1_cb, &nopen)<0) SS_ERROR(FAILED);
SS_ASSERT(nopen>0); /* because this file, which is File zero of the top scope, is explicitly open yet */
if (nopen>1) SS_ERROR_FMT(USAGE, ("%d file%s still open in %s", nopen-1, 2==nopen?"":"s", gfile->name));
/* Destroy the global blob table for this file */
if (ss_blob_desttab(gfile->gblob)<0) SS_ERROR(FAILED);
gfile->gblob = NULL;
/* Close the HDF5 file (fid==1 implies transient file). This is where all those scope groups and table datasets get
* closed since in the interest of less collectivity those functions just dropped the handles instead of closing them.
* See the H5F_CLOSE_STRONG property in ss_file_open(). */
if (gfile->fid>1 && H5Fclose(gfile->fid)<0) SS_ERROR(HDF5);
gfile->fid = 0;
/* Release other resources */
if (gfile->dxpl_independent>0 && H5Pclose(gfile->dxpl_independent)<0) SS_ERROR(HDF5);
gfile->dxpl_independent = 0;
if (gfile->dxpl_collective>0 && H5Pclose(gfile->dxpl_collective)<0) SS_ERROR(HDF5);
gfile->dxpl_collective = 0;
}
/* Decrement file open counter */
--gfile->cur_open;
SS_CLEANUP:
SS_LEAVE(0);
}