From 633182a6158aa4548e42c200887d62737a574b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20Lyche=20Melv=C3=A6r?= Date: Wed, 10 Jul 2019 21:16:39 +0200 Subject: [PATCH 1/5] Initial implementation --- include/netcdf.h | 1 + libhdf5/hdf5create.c | 24 ++++++++++++++++++---- libhdf5/hdf5open.c | 16 ++++++++++++++- libhdf5/hdf5var.c | 5 +++++ nc_test4/tst_files.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 5 deletions(-) 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 66fa0ac230..f39cac86b1 100644 --- a/libhdf5/hdf5create.c +++ b/libhdf5/hdf5create.c @@ -50,6 +50,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; @@ -157,12 +161,18 @@ 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; + if ((cmode & NC_HDF5_SWMR)) { + low = H5F_LIBVER_LATEST; + high = H5F_LIBVER_LATEST; + } #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. */ @@ -237,6 +247,12 @@ nc4_create_file(const char *path, int cmode, size_t initialsz, if ((retval = NC4_new_provenance(nc4_info))) BAIL(retval); + if ((cmode & NC_HDF5_SWMR)) { + /* Prepare for single writer multiple readers */ + if ((retval = H5Fstart_swmr_write(hdf5_info->hdfid))) + BAIL(retval); + } + return NC_NOERR; exit: /*failure exit*/ diff --git a/libhdf5/hdf5open.c b/libhdf5/hdf5open.c index 1b11855290..b8a6bd70c5 100644 --- a/libhdf5/hdf5open.c +++ b/libhdf5/hdf5open.c @@ -652,7 +652,14 @@ nc4_open_file(const char *path, int mode, void* parameters, NC *nc) LOG((3, "%s: path %s mode %d", __func__, path, mode)); assert(path && nc); - flags = (mode & NC_WRITE) ? H5F_ACC_RDWR : H5F_ACC_RDONLY; + if((mode & NC_WRITE)) { + flags = H5F_ACC_RDWR; + } else { + flags = H5F_ACC_RDONLY; + if((mode & NC_HDF5_SWMR)) { + flags |= H5F_ACC_SWMR_READ; + } + } /* Add necessary structs to hold netcdf-4 file data. */ if ((retval = nc4_nc4f_list_add(nc, path, mode))) @@ -829,6 +836,13 @@ nc4_open_file(const char *path, int mode, void* parameters, NC *nc) if (H5Pclose(fapl_id) < 0) BAIL(NC_EHDFERR); + /* Prepare for single writer multiple reader. */ + if (mode & NC_WRITE && mode & NC_HDF5_SWMR) { + if ((retval = H5Fstart_swmr_write(h5->hdfid))) { + BAIL(retval); + } + } + return NC_NOERR; exit: diff --git a/libhdf5/hdf5var.c b/libhdf5/hdf5var.c index 3ff19c4f62..e518e8d30e 100644 --- a/libhdf5/hdf5var.c +++ b/libhdf5/hdf5var.c @@ -1764,6 +1764,11 @@ NC4_put_vars(int ncid, int varid, const size_t *startp, const size_t *countp, return retval; if (range_error) return NC_ERANGE; + + /* Flush data for SWMR */ + if (H5Dflush(hdf5_var->hdf_datasetid) < 0) + BAIL(NC_EHDFERR); + return NC_NOERR; } diff --git a/nc_test4/tst_files.c b/nc_test4/tst_files.c index d8927d8ecb..d79f8be5a4 100644 --- a/nc_test4/tst_files.c +++ b/nc_test4/tst_files.c @@ -292,6 +292,54 @@ main(int argc, char **argv) if (nc_close(ncid)) ERR; } SUMMARIZE_ERR; + printf("*** testing HDF5 SWMR..."); + { +#define DATA_LEN 3 + + int ncid, ncid2, ncid3, varid, dimids[2]; + int ndims, nvars, natts, unlimdimid; + int dimids_var[1], var_type; + 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("tst_swmr.nc", 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("tst_swmr.nc", NC_HDF5_SWMR, &ncid)) ERR; + if (nc_close(ncid)) ERR; + + /* Open the file for SWMR writing, append data, and close. */ + if (nc_open("tst_swmr.nc", 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("tst_swmr.nc", 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; + } + SUMMARIZE_ERR; printf("*** testing CLASSIC_MODEL flag with classic formats..."); { int ncid; From 757207f53567cdfadb69fe77c7c1029cb3fe87cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20Lyche=20Melv=C3=A6r?= Date: Wed, 31 Jul 2019 10:07:45 +0200 Subject: [PATCH 2/5] Move up call to flush --- libhdf5/hdf5var.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libhdf5/hdf5var.c b/libhdf5/hdf5var.c index e518e8d30e..fe5b2fc9ce 100644 --- a/libhdf5/hdf5var.c +++ b/libhdf5/hdf5var.c @@ -1736,6 +1736,10 @@ 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); + /* Flush data for SWMR */ + if (H5Dflush(hdf5_var->hdf_datasetid) < 0) + BAIL(NC_EHDFERR); + /* Remember that we have written to this var so that Fill Value * can't be set for it. */ if (!var->written_to) @@ -1765,10 +1769,6 @@ NC4_put_vars(int ncid, int varid, const size_t *startp, const size_t *countp, if (range_error) return NC_ERANGE; - /* Flush data for SWMR */ - if (H5Dflush(hdf5_var->hdf_datasetid) < 0) - BAIL(NC_EHDFERR); - return NC_NOERR; } From 932d6e079c6a11478b30ac48476e420925ac5199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20Lyche=20Melv=C3=A6r?= Date: Wed, 31 Jul 2019 15:24:01 +0200 Subject: [PATCH 3/5] Expand test --- nc_test4/tst_files.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/nc_test4/tst_files.c b/nc_test4/tst_files.c index d79f8be5a4..5004f21fc5 100644 --- a/nc_test4/tst_files.c +++ b/nc_test4/tst_files.c @@ -296,9 +296,7 @@ main(int argc, char **argv) { #define DATA_LEN 3 - int ncid, ncid2, ncid3, varid, dimids[2]; - int ndims, nvars, natts, unlimdimid; - int dimids_var[1], var_type; + int ncid, ncid2, varid, dimids[2]; size_t time_len, beam_len; int i; int values[DATA_LEN]; @@ -309,18 +307,18 @@ main(int argc, char **argv) values[i] = DATA_LEN*2 - i; /* Create a file in SWMR mode for writing, create structure and close. */ - if (nc_create("tst_swmr.nc", NC_NETCDF4|NC_HDF5_SWMR, &ncid)) ERR; + 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("tst_swmr.nc", NC_HDF5_SWMR, &ncid)) ERR; + 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("tst_swmr.nc", NC_WRITE|NC_HDF5_SWMR, &ncid)) ERR; + 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; @@ -330,7 +328,7 @@ main(int argc, char **argv) if (nc_close(ncid)) ERR; /* Open the file for SWMR reading, verify data, and close. */ - if (nc_open("tst_swmr.nc", NC_HDF5_SWMR, &ncid)) ERR; + 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; @@ -338,6 +336,31 @@ main(int argc, char **argv) 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; printf("*** testing CLASSIC_MODEL flag with classic formats..."); From f0f3543d3ff170f62755e03f3e4f9fb7483bc32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20Lyche=20Melv=C3=A6r?= Date: Wed, 31 Jul 2019 16:23:28 +0200 Subject: [PATCH 4/5] Add flag H5F_ACC_SWMR_WRITE to H5Fcreate if we do not have H5P_set_libver_bounds --- libhdf5/hdf5create.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libhdf5/hdf5create.c b/libhdf5/hdf5create.c index f39cac86b1..7afee69b52 100644 --- a/libhdf5/hdf5create.c +++ b/libhdf5/hdf5create.c @@ -108,6 +108,11 @@ nc4_create_file(const char *path, int cmode, size_t initialsz, else flags = H5F_ACC_TRUNC; +#ifndef HAVE_H5PSET_LIBVER_BOUNDS + if (cmode & NC_HDF5_SWMR) + flags |= H5F_ACC_SWMR_WRITE; +#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) { From cd4cd05a7fcbaad411aab5996e0c276931dfadce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20Lyche=20Melv=C3=A6r?= Date: Tue, 6 Aug 2019 10:47:16 +0200 Subject: [PATCH 5/5] Protect usage of swmr by ifdefs --- CMakeLists.txt | 9 +++++++++ libhdf5/hdf5create.c | 6 ++++++ libhdf5/hdf5open.c | 4 ++++ libhdf5/hdf5var.c | 2 ++ nc_test4/tst_files.c | 2 ++ 5 files changed, 23 insertions(+) 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/libhdf5/hdf5create.c b/libhdf5/hdf5create.c index 7afee69b52..e92e553b35 100644 --- a/libhdf5/hdf5create.c +++ b/libhdf5/hdf5create.c @@ -108,9 +108,11 @@ 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, @@ -168,10 +170,12 @@ nc4_create_file(const char *path, int cmode, size_t initialsz, #if H5_VERSION_GE(1,10,2) 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 low = H5F_LIBVER_EARLIEST; high = H5F_LIBVER_LATEST; @@ -252,11 +256,13 @@ 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; diff --git a/libhdf5/hdf5open.c b/libhdf5/hdf5open.c index b8a6bd70c5..f3ad84767a 100644 --- a/libhdf5/hdf5open.c +++ b/libhdf5/hdf5open.c @@ -656,9 +656,11 @@ nc4_open_file(const char *path, int mode, void* parameters, NC *nc) 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. */ @@ -836,12 +838,14 @@ nc4_open_file(const char *path, int mode, void* parameters, NC *nc) 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; diff --git a/libhdf5/hdf5var.c b/libhdf5/hdf5var.c index 09748c8ecd..c18ec552af 100644 --- a/libhdf5/hdf5var.c +++ b/libhdf5/hdf5var.c @@ -1736,9 +1736,11 @@ 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. */ diff --git a/nc_test4/tst_files.c b/nc_test4/tst_files.c index 5004f21fc5..c7eea558d0 100644 --- a/nc_test4/tst_files.c +++ b/nc_test4/tst_files.c @@ -292,6 +292,7 @@ main(int argc, char **argv) if (nc_close(ncid)) ERR; } SUMMARIZE_ERR; +#ifdef HDF5_HAS_SWMR printf("*** testing HDF5 SWMR..."); { #define DATA_LEN 3 @@ -363,6 +364,7 @@ main(int argc, char **argv) } SUMMARIZE_ERR; +#endif /* HDF_HAS_SWMR */ printf("*** testing CLASSIC_MODEL flag with classic formats..."); { int ncid;