summaryrefslogtreecommitdiffstats
path: root/test/twriteorder.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/twriteorder.c')
-rw-r--r--test/twriteorder.c463
1 files changed, 463 insertions, 0 deletions
diff --git a/test/twriteorder.c b/test/twriteorder.c
new file mode 100644
index 0000000..58690f6
--- /dev/null
+++ b/test/twriteorder.c
@@ -0,0 +1,463 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. 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<nlinkedblock_g; i++){
+ /* calculate where to write this block */
+ blkaddr = i*part_size_g + i;
+ /* store old block address in byte 0-3 */
+ HDmemcpy(&buffer[0], &blkaddr_old, sizeof(blkaddr_old));
+ /* fill the rest with the lowest byte of i */
+ HDmemset(&buffer[4], i & 0xff, (size_t) (BLOCKSIZE_DFT-4));
+ /* write the block */
+#ifdef DEBUG
+ printf("writing block at %d\n", blkaddr);
+#endif
+ HDlseek(write_fd_g, (HDoff_t)blkaddr, SEEK_SET);
+ if ((ret_code=HDwrite(write_fd_g, buffer, (size_t)blocksize_g)) != blocksize_g){
+ printf("blkaddr write failed in partition %d\n", i);
+ return -1;
+ }
+ blkaddr_old = blkaddr;
+ }
+ /* write the last blkaddr in partition 0 */
+ HDlseek(write_fd_g, (HDoff_t)0, SEEK_SET);
+ if ((ret_code=HDwrite(write_fd_g, &blkaddr_old, (size_t)sizeof(blkaddr_old))) != sizeof(blkaddr_old)){
+ printf("blkaddr write failed in partition %d\n", 0);
+ return -1;
+ }
+
+ /* all writes done. return succeess. */
+#ifdef DEBUG
+ printf("wrote %d blocks\n", nlinkedblock_g);
+#endif
+ return 0;
+}
+
+int read_wo_file(void)
+{
+ int read_fd;
+ int blkaddr=0;
+ int ret_code;
+ int linkedblocks_read=0;
+ char buffer[BLOCKSIZE_DFT];
+
+ /* Open the data file */
+ if ((read_fd = HDopen(DATAFILE, O_RDONLY, 0)) < 0) {
+ printf("READER: error from open\n");
+ return -1;
+ }
+ /* keep reading the initial block address until it is non-zero before proceeding. */
+ while (blkaddr == 0){
+ HDlseek(read_fd, (HDoff_t)0, SEEK_SET);
+ if ((ret_code=HDread(read_fd, &blkaddr, (size_t)sizeof(blkaddr))) != sizeof(blkaddr)){
+ printf("blkaddr read failed in partition %d\n", 0);
+ return -1;
+ }
+ }
+ linkedblocks_read++;
+
+ /* got a non-zero blkaddr. Proceed down the linked blocks. */
+#ifdef DEBUG
+ printf("got initial block address=%d\n", blkaddr);
+#endif
+ while (blkaddr != 0){
+ HDlseek(read_fd, (HDoff_t)blkaddr, SEEK_SET);
+ if ((ret_code=HDread(read_fd, buffer, (size_t)blocksize_g)) != blocksize_g){
+ printf("blkaddr read failed in partition %d\n", 0);
+ return -1;
+ }
+ linkedblocks_read++;
+ /* retrieve the block address in byte 0-3 */
+ HDmemcpy(&blkaddr, &buffer[0], sizeof(blkaddr));
+#ifdef DEBUG
+ printf("got next block address=%d\n", blkaddr);
+#endif
+ }
+
+#ifdef DEBUG
+ printf("read %d blocks\n", linkedblocks_read);
+#endif
+ return 0;
+}
+
+
+/* Overall Algorithm:
+ * Parse options from user;
+ * Generate/pre-created the test file needed and close it;
+ * fork: child processes become the reader processes;
+ * while parent process continues as the writer process;
+ * both run till ending conditions are met.
+ */
+int
+main(int argc, char *argv[])
+{
+ /*pid_t childpid[READERS_MAX];
+ int child_ret_value[READERS_MAX];*/
+ pid_t childpid=0;
+ int child_ret_value;
+ pid_t mypid, tmppid;
+ int child_status;
+ int child_wait_option=0;
+ int ret_value = 0;
+
+ /* initialization */
+ if (setup_parameters(argc, argv) < 0){
+ Hgoto_error(1);
+ }
+
+ /* ==============================================================*/
+ /* UC_READWRITE: create datafile, launch both reader and writer. */
+ /* UC_WRITER: create datafile, skip reader, launch writer. */
+ /* UC_READER: skip create, launch reader, exit. */
+ /* ==============================================================*/
+ /* ============*/
+ /* Create file */
+ /* ============*/
+ if (launch_g != UC_READER){
+ printf("Creating skeleton data file for test...\n");
+ if (create_wo_file() < 0){
+ fprintf(stderr, "***encounter error\n");
+ Hgoto_error(1);
+ }else
+ printf("File created.\n");
+ }
+ /* flush output before possible fork */
+ HDfflush(stdout);
+
+ if (launch_g==UC_READWRITE){
+ /* fork process */
+ if((childpid = fork()) < 0) {
+ perror("fork");
+ Hgoto_error(1);
+ };
+ };
+ mypid = getpid();
+
+ /* ============= */
+ /* launch reader */
+ /* ============= */
+ if (launch_g != UC_WRITER){
+ /* child process launch the reader */
+ if(0 == childpid) {
+ printf("%d: launch reader process\n", mypid);
+ if (read_wo_file() < 0){
+ fprintf(stderr, "read_wo_file encountered error\n");
+ exit(1);
+ }
+ /* Reader is done. Clean up by removing the data file */
+ HDremove(DATAFILE);
+ exit(0);
+ }
+ }
+
+ /* ============= */
+ /* launch writer */
+ /* ============= */
+ /* this process continues to launch the writer */
+#ifdef DEBUG
+ printf("%d: continue as the writer process\n", mypid);
+#endif
+ if (write_wo_file() < 0){
+ fprintf(stderr, "write_wo_file encountered error\n");
+ Hgoto_error(1);
+ }
+
+ /* ================================================ */
+ /* If readwrite, collect exit code of child process */
+ /* ================================================ */
+ if (launch_g == UC_READWRITE){
+ if ((tmppid = waitpid(childpid, &child_status, child_wait_option)) < 0){
+ perror("waitpid");
+ Hgoto_error(1);
+ }
+ if (WIFEXITED(child_status)){
+ if ((child_ret_value=WEXITSTATUS(child_status)) != 0){
+ printf("%d: child process exited with non-zero code (%d)\n",
+ mypid, child_ret_value);
+ Hgoto_error(2);
+ }
+ } else {
+ printf("%d: child process terminated abnormally\n", mypid);
+ Hgoto_error(2);
+ }
+ }
+
+done:
+ /* Print result and exit */
+ if (ret_value != 0){
+ printf("Error(s) encountered\n");
+ }else{
+ printf("All passed\n");
+ }
+
+ return(ret_value);
+}
+
+#else /* H5_HAVE_FORK */
+
+int
+main(void)
+{
+ HDfprintf(stderr, "Non-POSIX platform. Skipping.\n");
+ return EXIT_SUCCESS;
+} /* end main() */
+
+#endif /* H5_HAVE_FORK */
+