diff --git a/CMakeLists.txt b/CMakeLists.txt index 3aa2ef73fb..c2d91a88ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}) @@ -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}) @@ -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() diff --git a/include/netcdf.h b/include/netcdf.h index 6ba8ba48be..8729e14151 100644 --- a/include/netcdf.h +++ b/include/netcdf.h @@ -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. */ diff --git a/libhdf5/hdf5create.c b/libhdf5/hdf5create.c index 39b3f3a614..d312fb55a1 100644 --- a/libhdf5/hdf5create.c +++ b/libhdf5/hdf5create.c @@ -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 + H5F_libver_t low, high; +#endif + #ifdef USE_PARALLEL4 NC_MPI_INFO *mpiinfo = NULL; MPI_Comm comm; @@ -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) { @@ -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; + } +#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. */ @@ -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*/ diff --git a/libhdf5/hdf5open.c b/libhdf5/hdf5open.c index 06d97a65b4..d556c50bf0 100644 --- a/libhdf5/hdf5open.c +++ b/libhdf5/hdf5open.c @@ -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))) @@ -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: diff --git a/libhdf5/hdf5var.c b/libhdf5/hdf5var.c index 2bf94f044c..c18ec552af 100644 --- a/libhdf5/hdf5var.c +++ b/libhdf5/hdf5var.c @@ -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) @@ -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; } diff --git a/nc_test4/tst_files.c b/nc_test4/tst_files.c index d8927d8ecb..c7eea558d0 100644 --- a/nc_test4/tst_files.c +++ b/nc_test4/tst_files.c @@ -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; +#endif /* HDF_HAS_SWMR */ printf("*** testing CLASSIC_MODEL flag with classic formats..."); { int ncid;