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