1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * Copyright by The HDF Group.                                               *
3  * All rights reserved.                                                      *
4  *                                                                           *
5  * This file is part of HDF5.  The full HDF5 copyright notice, including     *
6  * terms governing use, modification, and redistribution, is contained in    *
7  * the COPYING file, which can be found at the root of the source code       *
8  * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
9  * If you do not have access to either file, you may request a copy from     *
10  * help@hdfgroup.org.                                                        *
11  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12 
13 /***********************************************************
14 *
15 * Test program: twriteorder
16 *
17 * Test to verify that the write order is strictly consistent.
18 * The SWMR feature requires that the order of write is strictly consistent.
19 * "Strict consistency in computer science is the most stringent consistency
20 * model.  It says that a read operation has to return the result of the
21 * latest write operation which occurred on that data item."--
22 * (http://en.wikipedia.org/wiki/Linearizability#Definition_of_linearizability).
23 * This is also an alternative form of what POSIX write require that after a
24 * write operation has returned success, all reads issued afterward should
25 * get the same data the write has written.
26 *
27 * Created: Albert Cheng, 2013/8/28.
28 *************************************************************/
29 
30 /***********************************************************
31 *
32 * Algorithm
33 *
34 * The test simulates what SWMR does by writing chained blocks and see if
35 * they can be read back correctly.
36 * There is a writer process and multiple read processes.
37 * The file is divided into 2KB partitions. Then writer writes 1 chained
38 * block, each of 1KB big, in each partition after the first partition.
39 * Each chained block has this structure:
40 * Byte 0-3: offset address of its child block. The last child uses 0 as NULL.
41 * Byte 4-1023: some artificial data.
42 * The child block address of Block 1 is NULL (0).
43 * The child block address of Block 2 is the offset address of Block 1.
44 * The child block address of Block n is the offset address of Block n-1.
45 * After all n blocks are written, the offset address of Block n is written
46 * to the offset 0 of the first partition.
47 * Therefore, by the time the offset address of Block n is written to this
48 * position, all n chain-linked blocks have been written.
49 *
50 * The other reader processes will try to read the address value at the
51 * offset 0. The value is initially NULL(0). When it changes to non-zero,
52 * it signifies the writer process has written all the chain-link blocks
53 * and they are ready for the reader processes to access.
54 *
55 * If the system, in which the writer and reader processes run, the readers
56 * will always get all chain-linked blocks correctly. If the order of write
57 * is not maintained, some reader processes may found unexpect block data.
58 *
59 *************************************************************/
60 
61 #include "h5test.h"
62 
63 /* This test uses many POSIX things that are not available on
64  * Windows. We're using a check for fork(2) here as a proxy for
65  * all POSIX/Unix/Linux things until this test can be made
66  * more platform-independent.
67  */
68 #ifdef H5_HAVE_FORK
69 
70 #define DATAFILE   "twriteorder.dat"
71 /* #define READERS_MAX      10 */   /* max number of readers */
72 #define BLOCKSIZE_DFT       1024    /* 1KB */
73 #define PARTITION_DFT       2048    /* 2KB */
74 #define NLINKEDBLOCKS_DFT   512     /* default 512 */
75 #define SIZE_BLKADDR        4       /* expected sizeof blkaddr */
76 #define Hgoto_error(val)    {ret_value=val; goto done;}
77 
78 /* type declarations */
79 typedef enum part_t {
80     UC_READWRITE    = 0,    /* both writer and reader */
81     UC_WRITER,              /* writer only */
82     UC_READER               /* reader only */
83 } part_t;
84 
85 /* prototypes */
86 int create_wo_file(void);
87 int write_wo_file(void);
88 int read_wo_file(void);
89 void usage(const char *prog);
90 int setup_parameters(int argc, char * const argv[]);
91 int parse_option(int argc, char * const argv[]);
92 
93 /* Global Variable definitions */
94 const char *progname_g="twriteorder";   /* program name */
95 int write_fd_g;
96 int blocksize_g, part_size_g, nlinkedblock_g;
97 part_t  launch_g;
98 
99 /* Function definitions */
100 
101 /* Show help page */
102 void
usage(const char * prog)103 usage(const char *prog)
104 {
105     HDfprintf(stderr, "usage: %s [OPTIONS]\n", prog);
106     HDfprintf(stderr, "  OPTIONS\n");
107     HDfprintf(stderr, "     -h            Print a usage message and exit\n");
108     HDfprintf(stderr, "     -l w|r        launch writer or reader only. [default: launch both]\n");
109     HDfprintf(stderr, "     -b N          Block size [default: %d]\n", BLOCKSIZE_DFT);
110     HDfprintf(stderr, "     -p N          Partition size [default: %d]\n", PARTITION_DFT);
111     HDfprintf(stderr, "     -n N          Number of linked blocks [default: %d]\n", NLINKEDBLOCKS_DFT);
112     HDfprintf(stderr, "     where N is an integer value\n");
113     HDfprintf(stderr, "\n");
114 }
115 
116 /* Setup test parameters by parsing command line options.
117  * Setup default values if not set by options. */
118 int
parse_option(int argc,char * const argv[])119 parse_option(int argc, char * const argv[])
120 {
121     int ret_value=0;
122     int c;
123     /* command line options: See function usage for a description */
124     const char *cmd_options = "hb:l:n:p:";
125 
126     /* suppress getopt from printing error */
127     opterr = 0;
128 
129     while (1) {
130         c = getopt (argc, argv, cmd_options);
131         if (-1 == c)
132             break;
133 
134         switch (c) {
135             case 'h':
136                 usage(progname_g);
137                 HDexit(EXIT_SUCCESS);
138                 break;
139             case 'b':   /* number of planes to write/read */
140                 if ((blocksize_g = atoi(optarg)) <= 0) {
141                     HDfprintf(stderr, "bad blocksize %s, must be a positive integer\n", optarg);
142                     usage(progname_g);
143                     Hgoto_error(-1);
144                 };
145                 break;
146             case 'n':   /* number of planes to write/read */
147                 if ((nlinkedblock_g = atoi(optarg)) < 2) {
148                     HDfprintf(stderr, "bad number of linked blocks %s, must be greater than 1.\n", optarg);
149                     usage(progname_g);
150                     Hgoto_error(-1);
151                 };
152                 break;
153             case 'p':   /* number of planes to write/read */
154                 if ((part_size_g = atoi(optarg)) <= 0) {
155                     HDfprintf(stderr, "bad partition size %s, must be a positive integer\n", optarg);
156                     usage(progname_g);
157                     Hgoto_error(-1);
158                 };
159                 break;
160             case 'l':   /* launch reader or writer only */
161                 switch (*optarg) {
162                     case 'r':   /* reader only */
163                         launch_g = UC_READER;
164                         break;
165                     case 'w':   /* writer only */
166                         launch_g = UC_WRITER;
167                         break;
168                     default:
169                         HDfprintf(stderr, "launch value(%c) should be w or r only.\n", *optarg);
170                         usage(progname_g);
171                         Hgoto_error(-1);
172                         break;
173                 } /* end inner switch */
174                 HDprintf("launch = %d\n", launch_g);
175                 break;
176             case '?':
177                 HDfprintf(stderr, "getopt returned '%c'.\n", c);
178                 usage(progname_g);
179                 Hgoto_error(-1);
180             default:
181                 HDfprintf(stderr, "getopt returned unexpected value.\n");
182                 HDfprintf(stderr, "Unexpected value is %d\n", c);
183                 Hgoto_error(-1);
184         } /* end outer switch */
185     } /* end while */
186 
187     /* verify partition size must be >= blocksize */
188     if (part_size_g < blocksize_g ){
189         HDfprintf(stderr, "Blocksize %d should not be bigger than partition size %d\n", blocksize_g, part_size_g);
190         Hgoto_error(-1);
191     }
192 
193 done:
194     /* All done. */
195     return ret_value;
196 }
197 
198 /* Setup parameters for the test case.
199  * Return: 0 succeed; -1 fail.
200  */
setup_parameters(int argc,char * const argv[])201 int setup_parameters(int argc, char * const argv[])
202 {
203     /* test case defaults */
204     blocksize_g = BLOCKSIZE_DFT;
205     part_size_g = PARTITION_DFT;
206     nlinkedblock_g = NLINKEDBLOCKS_DFT;
207     launch_g = UC_READWRITE;
208 
209     /* parse options */
210     if (parse_option(argc, argv) < 0){
211         return -1;
212     }
213 
214     /* show parameters and return */
215     HDprintf("blocksize = %ld\n", (long)blocksize_g);
216     HDprintf("part_size = %ld\n", (long)part_size_g);
217     HDprintf("nlinkedblock = %ld\n", (long)nlinkedblock_g);
218     HDprintf("launch = %d\n", launch_g);
219 
220     return 0;
221 }
222 
223 /* Create the test file with initial "empty" file, that is,
224  * partition 0 has a null (0) address.
225  *
226  * Return: 0 succeed; -1 fail.
227  */
create_wo_file(void)228 int create_wo_file(void)
229 {
230     int                 blkaddr         = 0;    /* blkaddress of next linked block */
231     h5_posix_io_ret_t   bytes_wrote     = -1;   /* # of bytes written   */
232 
233     /* Create the data file */
234     if ((write_fd_g = HDopen(DATAFILE, O_RDWR|O_TRUNC|O_CREAT, H5_POSIX_CREATE_MODE_RW)) < 0) {
235         HDprintf("WRITER: error from open\n");
236         return -1;
237     }
238     blkaddr = 0;
239     /* write it to partition 0 */
240     if ((bytes_wrote = HDwrite(write_fd_g, &blkaddr, (size_t)SIZE_BLKADDR)) != SIZE_BLKADDR){
241         HDprintf("blkaddr write failed\n");
242         return -1;
243     }
244 
245     /* File initialized, return success */
246     return 0;
247 }
248 
write_wo_file(void)249 int write_wo_file(void)
250 {
251     int blkaddr;
252     int blkaddr_old=0;
253     int i;
254     char buffer[BLOCKSIZE_DFT];
255     h5_posix_io_ret_t   bytes_wrote     = -1;   /* # of bytes written   */
256 
257 
258     /* write block 1, 2, ... */
259     for (i = 1; i < nlinkedblock_g; i++) {
260 
261         /* calculate where to write this block */
262         blkaddr = i * part_size_g + i;
263 
264         /* store old block address in byte 0-3 */
265         HDmemcpy(&buffer[0], &blkaddr_old, sizeof(blkaddr_old));
266 
267         /* fill the rest with the lowest byte of i */
268         HDmemset(&buffer[4], i & 0xff, (size_t) (BLOCKSIZE_DFT-4));
269 
270         /* write the block */
271 #ifdef DEBUG
272         HDprintf("writing block at %d\n", blkaddr);
273 #endif
274         HDlseek(write_fd_g, (HDoff_t)blkaddr, SEEK_SET);
275         if ((bytes_wrote = HDwrite(write_fd_g, buffer, (size_t)blocksize_g)) != blocksize_g){
276             HDprintf("blkaddr write failed in partition %d\n", i);
277             return -1;
278         }
279 
280         blkaddr_old = blkaddr;
281 
282     } /* end for */
283 
284     /* write the last blkaddr in partition 0 */
285     HDlseek(write_fd_g, (HDoff_t)0, SEEK_SET);
286     if ((bytes_wrote = HDwrite(write_fd_g, &blkaddr_old, (size_t)sizeof(blkaddr_old))) != sizeof(blkaddr_old)){
287         HDprintf("blkaddr write failed in partition %d\n", 0);
288         return -1;
289     }
290 
291     /* all writes done. return succeess. */
292 #ifdef DEBUG
293     HDprintf("wrote %d blocks\n", nlinkedblock_g);
294 #endif
295     return 0;
296 }
297 
read_wo_file(void)298 int read_wo_file(void)
299 {
300     int read_fd;
301     int blkaddr = 0;
302     h5_posix_io_ret_t   bytes_read      = -1;   /* # of bytes actually read */
303     int linkedblocks_read = 0;
304     char buffer[BLOCKSIZE_DFT];
305 
306     /* Open the data file */
307     if ((read_fd = HDopen(DATAFILE, O_RDONLY)) < 0) {
308         HDprintf("READER: error from open\n");
309         return -1;
310     }
311 
312     /* keep reading the initial block address until it is non-zero before proceeding. */
313     while (blkaddr == 0) {
314         HDlseek(read_fd, (HDoff_t)0, SEEK_SET);
315         if ((bytes_read = HDread(read_fd, &blkaddr, (size_t)sizeof(blkaddr))) != sizeof(blkaddr)) {
316             HDprintf("blkaddr read failed in partition %d\n", 0);
317             return -1;
318         }
319     }
320     linkedblocks_read++;
321 
322     /* got a non-zero blkaddr. Proceed down the linked blocks. */
323 #ifdef DEBUG
324     HDprintf("got initial block address=%d\n", blkaddr);
325 #endif
326     while (blkaddr != 0) {
327         HDlseek(read_fd, (HDoff_t)blkaddr, SEEK_SET);
328         if ((bytes_read = HDread(read_fd, buffer, (size_t)blocksize_g)) != blocksize_g){
329             HDprintf("blkaddr read failed in partition %d\n", 0);
330             return -1;
331         }
332         linkedblocks_read++;
333 
334         /* retrieve the block address in byte 0-3 */
335         HDmemcpy(&blkaddr, &buffer[0], sizeof(blkaddr));
336 #ifdef DEBUG
337         HDprintf("got next block address=%d\n", blkaddr);
338 #endif
339     }
340 
341 #ifdef DEBUG
342     HDprintf("read %d blocks\n", linkedblocks_read);
343 #endif
344     return 0;
345 }
346 
347 
348 /* Overall Algorithm:
349  * Parse options from user;
350  * Generate/pre-created the test file needed and close it;
351  * fork: child processes become the reader processes;
352  *       while parent process continues as the writer process;
353  * both run till ending conditions are met.
354  */
355 int
main(int argc,char * argv[])356 main(int argc, char *argv[])
357 {
358     /*pid_t childpid[READERS_MAX];
359     int child_ret_value[READERS_MAX];*/
360     pid_t childpid=0;
361     int child_ret_value;
362     pid_t mypid, tmppid;
363     int child_status;
364     int child_wait_option=0;
365     int ret_value = 0;
366 
367     /* initialization */
368     if (setup_parameters(argc, argv) < 0){
369         Hgoto_error(1);
370     }
371 
372     /* ==============================================================*/
373     /* UC_READWRITE: create datafile, launch both reader and writer. */
374     /* UC_WRITER:    create datafile, skip reader, launch writer.    */
375     /* UC_READER:    skip create, launch reader, exit.               */
376     /* ==============================================================*/
377     /* ============*/
378     /* Create file */
379     /* ============*/
380     if (launch_g != UC_READER) {
381         HDprintf("Creating skeleton data file for test...\n");
382         if (create_wo_file() < 0) {
383             HDfprintf(stderr, "***encounter error\n");
384             Hgoto_error(1);
385         }
386         else
387             HDprintf("File created.\n");
388     }
389     /* flush output before possible fork */
390     HDfflush(stdout);
391 
392     if (launch_g==UC_READWRITE) {
393         /* fork process */
394         if((childpid = HDfork()) < 0) {
395             HDperror("fork");
396             Hgoto_error(1);
397         };
398     };
399     mypid = getpid();
400 
401     /* ============= */
402     /* launch reader */
403     /* ============= */
404     if (launch_g != UC_WRITER) {
405         /* child process launch the reader */
406         if(0 == childpid) {
407             HDprintf("%d: launch reader process\n", mypid);
408             if (read_wo_file() < 0) {
409                 HDfprintf(stderr, "read_wo_file encountered error\n");
410                 HDexit(EXIT_FAILURE);
411             }
412 
413             /* Reader is done. Clean up by removing the data file */
414             HDremove(DATAFILE);
415             HDexit(EXIT_SUCCESS);
416         }
417     }
418 
419     /* ============= */
420     /* launch writer */
421     /* ============= */
422     /* this process continues to launch the writer */
423 #ifdef DEBUG
424     HDprintf("%d: continue as the writer process\n", mypid);
425 #endif
426     if (write_wo_file() < 0) {
427         HDfprintf(stderr, "write_wo_file encountered error\n");
428         Hgoto_error(1);
429     }
430 
431     /* ================================================ */
432     /* If readwrite, collect exit code of child process */
433     /* ================================================ */
434     if (launch_g == UC_READWRITE) {
435         if ((tmppid = waitpid(childpid, &child_status, child_wait_option)) < 0) {
436             HDperror("waitpid");
437             Hgoto_error(1);
438         }
439         if (WIFEXITED(child_status)) {
440             if ((child_ret_value=WEXITSTATUS(child_status)) != 0) {
441                 HDprintf("%d: child process exited with non-zero code (%d)\n", mypid, child_ret_value);
442                 Hgoto_error(2);
443             }
444         }
445         else {
446             HDprintf("%d: child process terminated abnormally\n", mypid);
447             Hgoto_error(2);
448         }
449     }
450 
451 done:
452     /* Print result and exit */
453     if (ret_value != 0){
454         HDprintf("Error(s) encountered\n");
455     }
456     else {
457         HDprintf("All passed\n");
458     }
459 
460     return ret_value;
461 }
462 
463 #else /* H5_HAVE_FORK */
464 
465 int
main(void)466 main(void)
467 {
468     HDfprintf(stderr, "Non-POSIX platform. Skipping.\n");
469     return EXIT_SUCCESS;
470 } /* end main() */
471 
472 #endif /* H5_HAVE_FORK */
473 
474