/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by The HDF Group. * * All rights reserved. * * * * This file is part of HDF5. The full HDF5 copyright notice, including * * terms governing use, modification, and redistribution, is contained in * * the COPYING file, which can be found at the root of the source code * * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * * If you do not have access to either file, you may request a copy from * * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /*********************************************************** * * Test program: twriteorder * * Test to verify that the write order is strictly consistent. * The SWMR feature requires that the order of write is strictly consistent. * "Strict consistency in computer science is the most stringent consistency * model. It says that a read operation has to return the result of the * latest write operation which occurred on that data item."-- * (http://en.wikipedia.org/wiki/Linearizability#Definition_of_linearizability). * This is also an alternative form of what POSIX write require that after a * write operation has returned success, all reads issued afterward should * get the same data the write has written. * * Created: Albert Cheng, 2013/8/28. * Modified: *************************************************************/ /*********************************************************** * * Algorithm * * The test simulates what SWMR does by writing chained blocks and see if * they can be read back correctly. * There is a writer process and multiple read processes. * The file is divided into 2KB partitions. Then writer writes 1 chained * block, each of 1KB big, in each partition after the first partition. * Each chained block has this structure: * Byte 0-3: offset address of its child block. The last child uses 0 as NULL. * Byte 4-1023: some artificial data. * The child block address of Block 1 is NULL (0). * The child block address of Block 2 is the offset address of Block 1. * The child block address of Block n is the offset address of Block n-1. * After all n blocks are written, the offset address of Block n is written * to the offset 0 of the first partition. * Therefore, by the time the offset address of Block n is written to this * position, all n chain-linked blocks have been written. * * The other reader processes will try to read the address value at the * offset 0. The value is initially NULL(0). When it changes to non-zero, * it signifies the writer process has written all the chain-link blocks * and they are ready for the reader processes to access. * * If the system, in which the writer and reader processes run, the readers * will always get all chain-linked blocks correctly. If the order of write * is not maintained, some reader processes may found unexpect block data. * *************************************************************/ #include "h5test.h" /* This test uses many POSIX things that are not available on * Windows. We're using a check for fork(2) here as a proxy for * all POSIX/Unix/Linux things until this test can be made * more platform-independent. */ #ifdef H5_HAVE_FORK #define DATAFILE "twriteorder.dat" /* #define READERS_MAX 10 */ /* max number of readers */ #define BLOCKSIZE_DFT 1024 /* 1KB */ #define PARTITION_DFT 2048 /* 2KB */ #define NLINKEDBLOCKS_DFT 512 /* default 512 */ #define SIZE_BLKADDR 4 /* expected sizeof blkaddr */ #define Hgoto_error(val) {ret_value=val; goto done;} /* type declarations */ typedef enum part_t { UC_READWRITE =0, /* both writer and reader */ UC_WRITER, /* writer only */ UC_READER /* reader only */ } part_t; /* prototypes */ int create_wo_file(void); int write_wo_file(void); int read_wo_file(void); void usage(const char *prog); int setup_parameters(int argc, char * const argv[]); int parse_option(int argc, char * const argv[]); /* Global Variable definitions */ const char *progname_g="twriteorder"; /* program name */ int write_fd_g; int blocksize_g, part_size_g, nlinkedblock_g; part_t launch_g; /* Function definitions */ /* Show help page */ void usage(const char *prog) { fprintf(stderr, "usage: %s [OPTIONS]\n", prog); fprintf(stderr, " OPTIONS\n"); fprintf(stderr, " -h Print a usage message and exit\n"); fprintf(stderr, " -l w|r launch writer or reader only. [default: launch both]\n"); fprintf(stderr, " -b N Block size [default: %d]\n", BLOCKSIZE_DFT); fprintf(stderr, " -p N Partition size [default: %d]\n", PARTITION_DFT); fprintf(stderr, " -n N Number of linked blocks [default: %d]\n", NLINKEDBLOCKS_DFT); fprintf(stderr, " where N is an integer value\n"); fprintf(stderr, "\n"); } /* Setup test parameters by parsing command line options. * Setup default values if not set by options. */ int parse_option(int argc, char * const argv[]) { int ret_value=0; int c; /* command line options: See function usage for a description */ const char *cmd_options = "hb:l:n:p:"; /* suppress getopt from printing error */ opterr = 0; while (1){ c = getopt (argc, argv, cmd_options); if (-1 == c) break; switch (c) { case 'h': usage(progname_g); exit(0); break; case 'b': /* number of planes to write/read */ if ((blocksize_g = atoi(optarg)) <= 0){ fprintf(stderr, "bad blocksize %s, must be a positive integer\n", optarg); usage(progname_g); Hgoto_error(-1); }; break; case 'n': /* number of planes to write/read */ if ((nlinkedblock_g = atoi(optarg)) < 2){ fprintf(stderr, "bad number of linked blocks %s, must be greater than 1.\n", optarg); usage(progname_g); Hgoto_error(-1); }; break; case 'p': /* number of planes to write/read */ if ((part_size_g = atoi(optarg)) <= 0){ fprintf(stderr, "bad partition size %s, must be a positive integer\n", optarg); usage(progname_g); Hgoto_error(-1); }; break; case 'l': /* launch reader or writer only */ switch (*optarg) { case 'r': /* reader only */ launch_g = UC_READER; break; case 'w': /* writer only */ launch_g = UC_WRITER; break; default: fprintf(stderr, "launch value(%c) should be w or r only.\n", *optarg); usage(progname_g); Hgoto_error(-1); break; } printf("launch = %d\n", launch_g); break; case '?': fprintf(stderr, "getopt returned '%c'.\n", c); usage(progname_g); Hgoto_error(-1); default: fprintf(stderr, "getopt returned unexpected value.\n"); fprintf(stderr, "Unexpected value is %d\n", c); Hgoto_error(-1); } } /* verify partition size must be >= blocksize */ if (part_size_g < blocksize_g ){ fprintf(stderr, "Blocksize %d should not be bigger than partition size %d\n", blocksize_g, part_size_g); Hgoto_error(-1); } done: /* All done. */ return(ret_value); } /* Setup parameters for the test case. * Return: 0 succeed; -1 fail. */ int setup_parameters(int argc, char * const argv[]) { /* test case defaults */ blocksize_g = BLOCKSIZE_DFT; part_size_g = PARTITION_DFT; nlinkedblock_g = NLINKEDBLOCKS_DFT; launch_g = UC_READWRITE; /* parse options */ if (parse_option(argc, argv) < 0){ return(-1); } /* show parameters and return */ printf("blocksize = %ld\n", (long)blocksize_g); printf("part_size = %ld\n", (long)part_size_g); printf("nlinkedblock = %ld\n", (long)nlinkedblock_g); printf("launch = %d\n", launch_g); return(0); } /* Create the test file with initial "empty" file, that is, * partition 0 has a null (0) address. * * Return: 0 succeed; -1 fail. */ int create_wo_file(void) { int blkaddr=0; /* blkaddress of next linked block */ int ret_code; /* Create the data file */ if ((write_fd_g = HDopen(DATAFILE, O_RDWR|O_TRUNC|O_CREAT, 0664)) < 0) { printf("WRITER: error from open\n"); return -1; } blkaddr=0; /* write it to partition 0 */ if ((ret_code=HDwrite(write_fd_g, &blkaddr, (size_t)SIZE_BLKADDR)) != SIZE_BLKADDR){ printf("blkaddr write failed\n"); return -1; } /* File initialized, return success */ return 0; } int write_wo_file(void) { int blkaddr; int blkaddr_old=0; int i; char buffer[BLOCKSIZE_DFT]; int ret_code; /* write block 1, 2, ... */ for (i=1; i