From 5c5b72712788014d3debf7f08de8c9f03315b1c1 Mon Sep 17 00:00:00 2001 From: Dana Robinson <43805+derobins@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:22:40 -0700 Subject: [PATCH] Restore rand_r in a few parallel tests (#4749) The t_pmulti_dset and t_select_io_dset tests rely on the behavior of the previous private rand_r-like implementation to get the correct sequence of random numbers to pass. This has been restored using a fully private rand_r-like implementation that doesn't rely on rand_r and will work on Windows and other platforms where rand_r doesn't exist. --- test/h5test.c | 21 ++++++++++++++++ test/h5test.h | 8 ++++++ testpar/t_pmulti_dset.c | 51 ++++++++++++++++++++------------------ testpar/t_select_io_dset.c | 14 +++++------ 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/test/h5test.c b/test/h5test.c index 01216ca1059..61c01da529c 100644 --- a/test/h5test.c +++ b/test/h5test.c @@ -2563,3 +2563,24 @@ h5_driver_uses_multiple_files(const char *drv_name, unsigned flags) return ret_val; } + +/* Deterministic random number functions that don't modify the underlying + * C/POSIX library rand/random state, as this can cause spurious test failures. + * + * Adapted from the example code in the POSIX.1-2001 standard. + */ + +static unsigned int next_g = 1; + +int +h5_local_rand(void) +{ + next_g = next_g * 1103515245 + 12345; + return next_g & RAND_MAX; +} + +void +h5_local_srand(unsigned int seed) +{ + next_g = seed; +} diff --git a/test/h5test.h b/test/h5test.h index 238bd38acd6..1ec537c62e3 100644 --- a/test/h5test.h +++ b/test/h5test.h @@ -319,6 +319,14 @@ H5TEST_DLL herr_t h5_using_parallel_driver(hid_t fapl_id, bool *driver_i H5TEST_DLL herr_t h5_driver_is_default_vfd_compatible(hid_t fapl_id, bool *default_vfd_compatible); H5TEST_DLL bool h5_driver_uses_multiple_files(const char *drv_name, unsigned flags); +/* Random number functions that don't modify the underlying rand/random state. + * These use rand_r with a state pointer under the hood. The state is always + * initialized to the same value so that each process in the parallel tests + * always gets the same sequence. + */ +H5TEST_DLL int h5_local_rand(void); +H5TEST_DLL void h5_local_srand(unsigned int seed); + /* Functions that will replace components of a FAPL */ H5TEST_DLL herr_t h5_get_vfd_fapl(hid_t fapl_id); H5TEST_DLL herr_t h5_get_libver_fapl(hid_t fapl_id); diff --git a/testpar/t_pmulti_dset.c b/testpar/t_pmulti_dset.c index 622690dbece..e9819840cb3 100644 --- a/testpar/t_pmulti_dset.c +++ b/testpar/t_pmulti_dset.c @@ -267,7 +267,7 @@ test_pmdset(size_t niter, unsigned flags) for (i = 0; i < niter; i++) { /* Determine number of datasets */ ndsets = (flags & MDSET_FLAG_MLAYOUT) ? 3 - : (flags & MDSET_FLAG_MDSET) ? (size_t)((size_t)rand() % max_dsets) + 1 + : (flags & MDSET_FLAG_MDSET) ? (size_t)((size_t)h5_local_rand() % max_dsets) + 1 : 1; /* Create file */ @@ -280,16 +280,16 @@ test_pmdset(size_t niter, unsigned flags) (flags & MDSET_FLAG_CHUNK) || ((flags & MDSET_FLAG_MLAYOUT) && (j == 1 || j == 2)); /* Generate file dataspace */ - dset_dims[j][0] = (hsize_t)((rand() % MAX_DSET_X) + 1); - dset_dims[j][1] = (hsize_t)((rand() % MAX_DSET_Y) + 1); + dset_dims[j][0] = (hsize_t)((h5_local_rand() % MAX_DSET_X) + 1); + dset_dims[j][1] = (hsize_t)((h5_local_rand() % MAX_DSET_Y) + 1); if ((file_space_ids[j] = H5Screate_simple(2, dset_dims[j], use_chunk ? max_dims : NULL)) < 0) T_PMD_ERROR; /* Generate chunk if called for by configuration (multi layout uses chunked for datasets * 1 and 2) */ if (use_chunk) { - chunk_dims[0] = (hsize_t)((rand() % MAX_CHUNK_X) + 1); - chunk_dims[1] = (hsize_t)((rand() % MAX_CHUNK_Y) + 1); + chunk_dims[0] = (hsize_t)((h5_local_rand() % MAX_CHUNK_X) + 1); + chunk_dims[1] = (hsize_t)((h5_local_rand() % MAX_CHUNK_Y) + 1); if (H5Pset_chunk(dcpl_id[j], 2, chunk_dims) < 0) T_PMD_ERROR; } /* end if */ @@ -297,10 +297,10 @@ test_pmdset(size_t niter, unsigned flags) /* Create dataset */ /* If MDSET_FLAG_TCONV is set, use a different datatype with 50% probability, so * some datasets require type conversion and others do not */ - if ((dset_ids[j] = - H5Dcreate2(file_id, dset_name[j], - (flags & MDSET_FLAG_TCONV && rand() % 2) ? H5T_NATIVE_LONG : H5T_NATIVE_UINT, - file_space_ids[j], H5P_DEFAULT, dcpl_id[j], H5P_DEFAULT)) < 0) + if ((dset_ids[j] = H5Dcreate2(file_id, dset_name[j], + (flags & MDSET_FLAG_TCONV && h5_local_rand() % 2) ? H5T_NATIVE_LONG + : H5T_NATIVE_UINT, + file_space_ids[j], H5P_DEFAULT, dcpl_id[j], H5P_DEFAULT)) < 0) T_PMD_ERROR; } /* end for */ @@ -325,7 +325,7 @@ test_pmdset(size_t niter, unsigned flags) /* Perform read/write operations */ for (j = 0; j < OPS_PER_FILE; j++) { /* Decide whether to read or write */ - do_read = (bool)(rand() % 2); + do_read = (bool)(h5_local_rand() % 2); /* Barrier to ensure processes have finished the previous operation */ @@ -387,9 +387,9 @@ test_pmdset(size_t niter, unsigned flags) (int)((unsigned)max_dsets * MAX_DSET_X * MAX_DSET_Y) * ((int)l - (int)mpi_rank); /* Decide whether to do a hyperslab or point selection */ - if (rand() % 2) { + if (h5_local_rand() % 2) { /* Hyperslab */ - size_t nhs = (size_t)((rand() % MAX_HS) + 1); /* Number of hyperslabs */ + size_t nhs = (size_t)((h5_local_rand() % MAX_HS) + 1); /* Number of hyperslabs */ size_t max_hs_x = (MAX_HS_X <= dset_dims[k][0]) ? MAX_HS_X : dset_dims[k][0]; /* Determine maximum hyperslab size in X */ @@ -401,14 +401,16 @@ test_pmdset(size_t niter, unsigned flags) overlap = true; for (n = 0; overlap && (n < MAX_SEL_RETRIES); n++) { /* Generate hyperslab */ - count[m][0] = (hsize_t)(((hsize_t)rand() % max_hs_x) + 1); - count[m][1] = (hsize_t)(((hsize_t)rand() % max_hs_y) + 1); - start[m][0] = (count[m][0] == dset_dims[k][0]) - ? 0 - : (hsize_t)rand() % (dset_dims[k][0] - count[m][0] + 1); - start[m][1] = (count[m][1] == dset_dims[k][1]) - ? 0 - : (hsize_t)rand() % (dset_dims[k][1] - count[m][1] + 1); + count[m][0] = (hsize_t)(((hsize_t)h5_local_rand() % max_hs_x) + 1); + count[m][1] = (hsize_t)(((hsize_t)h5_local_rand() % max_hs_y) + 1); + start[m][0] = + (count[m][0] == dset_dims[k][0]) + ? 0 + : (hsize_t)h5_local_rand() % (dset_dims[k][0] - count[m][0] + 1); + start[m][1] = + (count[m][1] == dset_dims[k][1]) + ? 0 + : (hsize_t)h5_local_rand() % (dset_dims[k][1] - count[m][1] + 1); /* If writing, check for overlap with other processes */ overlap = false; @@ -460,7 +462,8 @@ test_pmdset(size_t niter, unsigned flags) } /* end if */ else { /* Point selection */ - size_t npoints = (size_t)(((size_t)rand() % MAX_POINTS) + 1); /* Number of points */ + size_t npoints = + (size_t)(((size_t)h5_local_rand() % MAX_POINTS) + 1); /* Number of points */ /* Reset dataset usage array if reading, since in this case we don't care * about overlapping selections between processes */ @@ -472,8 +475,8 @@ test_pmdset(size_t niter, unsigned flags) overlap = true; for (n = 0; overlap && (n < MAX_SEL_RETRIES); n++) { /* Generate point */ - points[2 * m] = (unsigned)((hsize_t)rand() % dset_dims[k][0]); - points[(2 * m) + 1] = (unsigned)((hsize_t)rand() % dset_dims[k][1]); + points[2 * m] = (unsigned)((hsize_t)h5_local_rand() % dset_dims[k][0]); + points[(2 * m) + 1] = (unsigned)((hsize_t)h5_local_rand() % dset_dims[k][1]); /* Check for overlap with other processes (write) or this process * (always) */ @@ -664,7 +667,7 @@ main(int argc, char *argv[]) /* Seed random number generator with shared seed (so all ranks generate the * same sequence) */ - srand(seed); + h5_local_srand(seed); /* Fill dset_name array */ for (i = 0; i < MAX_DSETS; i++) { diff --git a/testpar/t_select_io_dset.c b/testpar/t_select_io_dset.c index 271d38c7fbd..a6e62c6f8e5 100644 --- a/testpar/t_select_io_dset.c +++ b/testpar/t_select_io_dset.c @@ -1588,7 +1588,7 @@ test_multi_dsets_no_bkg(hid_t fid, unsigned chunked, unsigned dtrans, unsigned s mwbuf ? "mwbuf" : "nomwbuf"); /* Flip a coin to see if we're doing type conversion */ - tconv = rand() % 2; + tconv = h5_local_rand() % 2; if (tconv) any_tconv = true; @@ -2079,7 +2079,7 @@ test_multi_dsets_cmpd_with_bkg(hid_t fid, unsigned chunked, unsigned select, uns } /* Case c */ - mm = rand() % (int)ndsets; + mm = h5_local_rand() % (int)ndsets; if (!mm) mm++; @@ -2719,9 +2719,9 @@ test_multi_dsets_conv_sel_empty(hid_t fid, unsigned chunked, unsigned dtrans, un P_TEST_ERROR; } else { - if ((dset_dids[i] = - H5Dcreate2(fid, dset_names[i], ((rand() % 2) ? H5T_NATIVE_LLONG : H5T_NATIVE_SHORT), - file_sids[i], H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) + if ((dset_dids[i] = H5Dcreate2(fid, dset_names[i], + ((h5_local_rand() % 2) ? H5T_NATIVE_LLONG : H5T_NATIVE_SHORT), + file_sids[i], H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) P_TEST_ERROR; } } @@ -2790,7 +2790,7 @@ test_multi_dsets_conv_sel_empty(hid_t fid, unsigned chunked, unsigned dtrans, un * process 0: get 0 row; other processes: hyperslab */ - mm = rand() % (int)ndsets; + mm = h5_local_rand() % (int)ndsets; if (mm == 0) mm++; @@ -3169,7 +3169,7 @@ test_multi_dsets_all(int niter, hid_t fid, unsigned chunked, unsigned select, un if ((mem_sids[i] = H5Screate_simple(1, block, NULL)) < 0) P_TEST_ERROR; - mm = rand() % (int)ndsets; + mm = h5_local_rand() % (int)ndsets; if (mm == 0) { dset_types[i] = DSET_WITH_NO_CONV; snprintf(dset_names[i], sizeof(dset_names[i]), "multi_all_nconv_dset%d_%s_%s_%s", i,