Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SWMR in HDF5 #1448

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,8 @@ IF(ENABLE_HDF4)
ENDIF()
ENDIF()

OPTION(ENABLE_HDF5_SWMR "Support SWMR in HDF5. This requires HDF version 1.10 or later" OFF)

# Option to Build DLL
IF(WIN32)
OPTION(ENABLE_DLL "Build a Windows DLL." ${BUILD_SHARED_LIBS})
Expand Down Expand Up @@ -629,6 +631,10 @@ IF(USE_HDF5 OR ENABLE_NETCDF_4)
# Assert HDF5 version meets minimum required version.
##
SET(HDF5_VERSION_REQUIRED 1.8.10)
IF(ENABLE_HDF5_SWMR_SUPPORT)
SET(HDF5_VERSION_REQUIRED 1.10)
MESSAGE(STATUS "HDF5 SWMR is enabled. This implies that HDF5 version at least ${HDF5_VERSION_REQUIRED} is required.")
ENDIF()

IF(HDF5_VERSION_STRING AND NOT HDF5_VERSION)
SET(HDF5_VERSION ${HDF5_VERSION_STRING})
Expand All @@ -642,6 +648,9 @@ IF(USE_HDF5 OR ENABLE_NETCDF_4)
"netCDF requires at least HDF5 ${HDF5_VERSION_REQUIRED}. Found ${HDF5_VERSION}.")
ELSE()
MESSAGE(STATUS "Found HDF5 libraries version ${HDF5_VERSION}")
IF(ENABLE_HDF5_SWMR_SUPPORT)
ADD_DEFINITIONS(-DHDF5_HAS_SWMR)
ENDIF()
ENDIF()
ENDIF()

Expand Down
1 change: 1 addition & 0 deletions include/netcdf.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ Use this in mode flags for both nc_create() and nc_open(). */

#define NC_PERSIST 0x4000 /**< Save diskless contents to disk. Mode flag for nc_open() or nc_create() */
#define NC_INMEMORY 0x8000 /**< Read from memory. Mode flag for nc_open() or nc_create() */
#define NC_HDF5_SWMR 0x2000 /** Reuse deprecated MPIIO flag for SWMR **/

#define NC_MAX_MAGIC_NUMBER_LEN 8 /**< Max len of user-defined format magic number. */

Expand Down
35 changes: 31 additions & 4 deletions libhdf5/hdf5create.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ nc4_create_file(const char *path, int cmode, size_t initialsz,
NC_HDF5_FILE_INFO_T *hdf5_info;
NC_HDF5_GRP_INFO_T *hdf5_grp;

#ifdef HAVE_H5PSET_LIBVER_BOUNDS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we ever not have H5PSET_LIBVER_BOUNDS?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was introduced in HDF5-1.8.0

H5F_libver_t low, high;
#endif

#ifdef USE_PARALLEL4
NC_MPI_INFO *mpiinfo = NULL;
MPI_Comm comm;
Expand Down Expand Up @@ -105,6 +109,13 @@ nc4_create_file(const char *path, int cmode, size_t initialsz,
else
flags = H5F_ACC_TRUNC;

#ifdef HDF5_HAS_SWMR
#ifndef HAVE_H5PSET_LIBVER_BOUNDS
if (cmode & NC_HDF5_SWMR)
flags |= H5F_ACC_SWMR_WRITE;
#endif
#endif

/* If this file already exists, and NC_NOCLOBBER is specified,
return an error (unless diskless|inmemory) */
if (!nc4_info->mem.diskless && !nc4_info->mem.inmemory) {
Expand Down Expand Up @@ -158,12 +169,20 @@ nc4_create_file(const char *path, int cmode, size_t initialsz,

#ifdef HAVE_H5PSET_LIBVER_BOUNDS
#if H5_VERSION_GE(1,10,2)
if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_EARLIEST, H5F_LIBVER_V18) < 0)
low = H5F_LIBVER_EARLIEST;
high = H5F_LIBVER_V18;
#ifdef HDF5_HAS_SWMR
if ((cmode & NC_HDF5_SWMR)) {
low = H5F_LIBVER_LATEST;
high = H5F_LIBVER_LATEST;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this create files that are unreadable by older versions of HDF5?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that H5F_LIBVER_V18 was introduced in 1.10.2 so maybe the HAVE_H5PSET_LIBVER_BOUNDS is checking for that version and not for existance of H5Pset_libver_bounds

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the files will be unreadable with 1.8.X clients. Here is a blurb from 1.10.2 release notes:

When it is used in application linked with HDF5 1.10.0, it will enable new chunk indexing for Single Writer/Multiple Reader (SWMR) access and Virtual Dataset storage.

What does this change mean to an HDF5 application?
When an HDF5 application linked with HDF5 1.10.2 specifies H5F_LIBVER_LATEST as a value for the “high” parameter, the application may produce files that are not compatible with the HDF5 1.8.* file format. For example, new chunk indexing will be used that was not known to HDF5 1.8.. This means that an application linked with HDF5 1.8. libraries may not be able to read such files.

When an HDF5 application linked with HDF5 1.10.2 specifies H5F_LIBVER_V18 as a value for the “high” parameter, the application will produce files fully compatible with HDF5 1.8., meaning that any application linked with the HDF5 1.8. libraries will be able to read such files.

}
#endif /* HDF5_HAS_SWMR */
#else
if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_EARLIEST,
H5F_LIBVER_LATEST) < 0)
low = H5F_LIBVER_EARLIEST;
high = H5F_LIBVER_LATEST;
#endif
BAIL(NC_EHDFERR);
if (H5Pset_libver_bounds(fapl_id, low, high) < 0)
BAIL(NC_EHDFERR);
#endif

/* Create the property list. */
Expand Down Expand Up @@ -238,6 +257,14 @@ nc4_create_file(const char *path, int cmode, size_t initialsz,
if ((retval = NC4_new_provenance(nc4_info)))
BAIL(retval);

#ifdef HDF5_HAS_SWMR
if ((cmode & NC_HDF5_SWMR)) {
/* Prepare for single writer multiple readers */
if ((retval = H5Fstart_swmr_write(hdf5_info->hdfid)))
BAIL(retval);
}
#endif

return NC_NOERR;

exit: /*failure exit*/
Expand Down
20 changes: 19 additions & 1 deletion libhdf5/hdf5open.c
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,16 @@ nc4_open_file(const char *path, int mode, void* parameters, int ncid)
assert(nc && nc->model->impl == NC_FORMATX_NC4);

/* Determine the HDF5 open flag to use. */
flags = (mode & NC_WRITE) ? H5F_ACC_RDWR : H5F_ACC_RDONLY;
if((mode & NC_WRITE)) {
flags = H5F_ACC_RDWR;
} else {
flags = H5F_ACC_RDONLY;
#ifdef HDF5_HAS_SWMR
if((mode & NC_HDF5_SWMR)) {
flags |= H5F_ACC_SWMR_READ;
}
#endif
}

/* Add necessary structs to hold netcdf-4 file data. */
if ((retval = nc4_nc4f_list_add(nc, path, mode)))
Expand Down Expand Up @@ -835,6 +844,15 @@ nc4_open_file(const char *path, int mode, void* parameters, int ncid)
if (H5Pclose(fapl_id) < 0)
BAIL(NC_EHDFERR);

#ifdef HDF5_HAS_SWMR
/* Prepare for single writer multiple reader. */
if (mode & NC_WRITE && mode & NC_HDF5_SWMR) {
if ((retval = H5Fstart_swmr_write(h5->hdfid))) {
BAIL(retval);
}
}
#endif

return NC_NOERR;

exit:
Expand Down
7 changes: 7 additions & 0 deletions libhdf5/hdf5var.c
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,12 @@ NC4_put_vars(int ncid, int varid, const size_t *startp, const size_t *countp,
mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0)
BAIL(NC_EHDFERR);

#ifdef HDF5_HAS_SWMR
/* Flush data for SWMR */
if (H5Dflush(hdf5_var->hdf_datasetid) < 0)
BAIL(NC_EHDFERR);
#endif

/* Remember that we have written to this var so that Fill Value
* can't be set for it. */
if (!var->written_to)
Expand Down Expand Up @@ -1764,6 +1770,7 @@ NC4_put_vars(int ncid, int varid, const size_t *startp, const size_t *countp,
return retval;
if (range_error)
return NC_ERANGE;

return NC_NOERR;
}

Expand Down
73 changes: 73 additions & 0 deletions nc_test4/tst_files.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,79 @@ main(int argc, char **argv)
if (nc_close(ncid)) ERR;
}
SUMMARIZE_ERR;
#ifdef HDF5_HAS_SWMR
printf("*** testing HDF5 SWMR...");
{
#define DATA_LEN 3

int ncid, ncid2, varid, dimids[2];
size_t time_len, beam_len;
int i;
int values[DATA_LEN];
size_t start[2] = {0,0}, count[2] = {1, DATA_LEN};

/* Initialize some phony data. */
for (i = 0; i < DATA_LEN; i++)
values[i] = DATA_LEN*2 - i;

/* Create a file in SWMR mode for writing, create structure and close. */
if (nc_create(FILE_NAME, NC_NETCDF4|NC_HDF5_SWMR, &ncid)) ERR;
if (nc_def_dim(ncid, "time", NC_UNLIMITED, &dimids[0])) ERR;
if (nc_def_dim(ncid, "beam", NC_UNLIMITED, &dimids[1])) ERR;
if (nc_def_var(ncid, "depth", NC_INT, 2, dimids, &varid)) ERR;
if (nc_close(ncid)) ERR;

/* Open the file for SWMR reading and close. */
if (nc_open(FILE_NAME, NC_HDF5_SWMR, &ncid)) ERR;
if (nc_close(ncid)) ERR;

/* Open the file for SWMR writing, append data, and close. */
if (nc_open(FILE_NAME, NC_WRITE|NC_HDF5_SWMR, &ncid)) ERR;
if (nc_inq_varid(ncid, "depth", &varid)) ERR;
if (nc_put_vara_int(ncid, varid, start, count, values)) ERR;
if (nc_inq_dimlen(ncid, dimids[0], &time_len)) ERR;
if (time_len != 1) ERR;
if (nc_inq_dimlen(ncid, dimids[1], &beam_len)) ERR;
if (beam_len != DATA_LEN) ERR;
if (nc_close(ncid)) ERR;

/* Open the file for SWMR reading, verify data, and close. */
if (nc_open(FILE_NAME, NC_HDF5_SWMR, &ncid)) ERR;
if (nc_inq_varid(ncid, "depth", &varid)) ERR;
if (nc_put_vara_int(ncid, varid, start, count, values) == 0) ERR; // Writing should fail
if (nc_inq_dimlen(ncid, dimids[0], &time_len)) ERR;
if (time_len != 1) ERR;
if (nc_inq_dimlen(ncid, dimids[1], &beam_len)) ERR;
if (beam_len != DATA_LEN) ERR;
if (nc_close(ncid)) ERR;

/* Append data to the file from one writer (ncid1) and verify from a reader (ncid2) */
if (nc_open(FILE_NAME, NC_WRITE|NC_HDF5_SWMR, &ncid)) ERR;
if (nc_open(FILE_NAME, NC_HDF5_SWMR, &ncid2)) ERR;

// Verify length of time dimension == 1 in both reader and writer
if (nc_inq_dimlen(ncid, dimids[0], &time_len)) ERR;
if (time_len != 1) ERR;
if (nc_inq_dimlen(ncid2, dimids[0], &time_len)) ERR;
if (time_len != 1) ERR;

// Append data
start[0] = 1; start[1] = 0;
if (nc_inq_varid(ncid, "depth", &varid)) ERR;
if (nc_put_vara_int(ncid, varid, start, count, values)) ERR;

// Verify length of time dimension == 2 in both reader and writer
if (nc_inq_dimlen(ncid, dimids[0], &time_len)) ERR;
if (time_len != 2) ERR;
if (nc_inq_dimlen(ncid2, dimids[0], &time_len)) ERR;
if (time_len != 2) ERR;

if (nc_close(ncid)) ERR;
if (nc_close(ncid2)) ERR;

}
SUMMARIZE_ERR;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent test, thanks!

#endif /* HDF_HAS_SWMR */
printf("*** testing CLASSIC_MODEL flag with classic formats...");
{
int ncid;
Expand Down