/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Use Case 1.8 Appending a hyperslab of multiple chunks.
 * Description:
 *     Appending a hyperslab that spans several chunks of a dataset with
 *     unlimited dimensions within a pre-created file and reading the new
 *     data back.
 * Goal:
 *     Read data appended by the Writer to a pre-existing dataset in a
 *     file. The dataset has one or more unlimited dimensions. The data
 *     is appended by a hyperslab that is contained in several chunks (for
 *     example, appending 2-dim planes along the slowest changing dimension
 *     in the 3-dim dataset and each plane is covered by 4 chunks).
 * Level:
 *     User Level
 * Guarantees:
 *   o Readers will see the modified dimension sizes after the Writer
 *     finishes HDF5 metadata updates and issues H5Fflush or H5Oflush calls.
 *   o Readers will see newly appended data after the Writer finishes
 *     the flush operation.
 * 
 * Preconditions:
 *   o Readers are not allowed to modify the file.
 *   o All datasets that are modified by the Writer exist when the
 *     Writer opens the file.
 *   o All datasets that are modified by the Writer exist when a Reader
 *     opens the file.
 * 
 * Main Success Scenario:
 *  1. An application creates a file with required objects (groups,
 *     datasets, and attributes).
 *  2. The Writer opens the file and datasets in the file and starts
 *     adding data using H5Dwrite call with a hyperslab selection that
 *     spans several chunks.
 *  3. A Reader opens the file and a dataset in a file; if the size of
 *     the unlimited dimension has changed, reads the appended data back.
 * 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* Created: Albert Cheng, 2013/6/1 */

#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

#include "use.h"

/* Global Variable definitions */
options_t UC_opts;	/* Use Case Options */
const char *progname_g="use_append_mchunks";	/* program name */

/* Setup parameters for the use case.
 * Return: 0 succeed; -1 fail.
 */
int setup_parameters(int argc, char * const argv[])
{
    /* use case defaults */
    HDmemset(&UC_opts, 0, sizeof(options_t));
    UC_opts.chunksize = Chunksize_DFT;
    UC_opts.use_swmr = 1;	/* use swmr open */
    UC_opts.iterations = 1;
    UC_opts.chunkplanes = 1;

    /* parse options */
    if (parse_option(argc, argv) < 0){
	return(-1);
    }
    /* set chunk dims */
    UC_opts.chunkdims[0] = (hsize_t)UC_opts.chunkplanes;
    UC_opts.chunkdims[1] = UC_opts.chunkdims[2] = (hsize_t)UC_opts.chunksize;

    /* set dataset initial and max dims */
    UC_opts.dims[0] = 0;
    UC_opts.max_dims[0] = H5S_UNLIMITED;
    UC_opts.dims[1] = UC_opts.dims[2] = UC_opts.max_dims[1] = UC_opts.max_dims[2] = 2 * (hsize_t)UC_opts.chunksize;

    /* set nplanes */
    if (UC_opts.nplanes == 0)
        UC_opts.nplanes = 2 * (hsize_t)UC_opts.chunksize;

    /* show parameters and return */
    show_parameters();
    return(0);
}


/* Overall Algorithm: 
 * Parse options from user;
 * Generate/pre-created test files needed and close it;
 * fork: child process becomes the reader process;
 *       while parent process continues as the writer process;
 * both run till ending conditions are met.
 */
int
main(int argc, char *argv[])
{
    pid_t childpid=0;
    pid_t mypid, tmppid;
    int	child_status;
    int child_wait_option=0;
    int ret_value = 0;
    int child_ret_value;
    hbool_t send_wait = 0;
    hid_t fapl = -1;        /* File access property list */
    hid_t fid = -1;         /* File ID */
    char *name;             /* Test file name */

    /* initialization */
    if (setup_parameters(argc, argv) < 0){
        Hgoto_error(1);
    }

    /* Determine the need to send/wait message file*/
    if(UC_opts.launch == UC_READWRITE) {
        HDunlink(WRITER_MESSAGE);
        send_wait = 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 (UC_opts.launch != UC_READER){
        HDprintf("Creating skeleton data file for test...\n");
	if (create_uc_file() < 0){
	    HDfprintf(stderr, "***encounter error\n");
	    Hgoto_error(1);
	}else
	    HDprintf("File created.\n");
    }

    if (UC_opts.launch==UC_READWRITE){
        /* fork process */
        if((childpid = fork()) < 0) {
            perror("fork");
            Hgoto_error(1);
        };
    };
    mypid = getpid();

    /* ============= */
    /* launch reader */
    /* ============= */
    if (UC_opts.launch != UC_WRITER){
        /* child process launch the reader */
        if(0 == childpid) {
            HDprintf("%d: launch reader process\n", mypid);
            if (read_uc_file(send_wait) < 0){
                HDfprintf(stderr, "read_uc_file encountered error\n");
                HDexit(1);
            }
            HDexit(0);
        }
    }

    /* ============= */
    /* launch writer */
    /* ============= */
    /* this process continues to launch the writer */
    HDprintf("%d: continue as the writer process\n", mypid);

    name = UC_opts.filename;

    /* Set the file access property list */
    if((fapl = h5_fileaccess()) < 0)
        Hgoto_error(1);

    if(UC_opts.use_swmr)
        if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
            Hgoto_error(1);

    /* Open the file */
    if((fid = H5Fopen(name, H5F_ACC_RDWR | (UC_opts.use_swmr ? H5F_ACC_SWMR_WRITE : 0), fapl)) < 0) {
        HDfprintf(stderr, "H5Fopen failed\n");
        Hgoto_error(1);
    }

    if(write_uc_file(send_wait, fid) < 0) {
        HDfprintf(stderr, "write_uc_file encountered error\n");
        Hgoto_error(1);
    }


    /* ================================================ */
    /* If readwrite, collect exit code of child process */
    /* ================================================ */
    if (UC_opts.launch == UC_READWRITE){
        if ((tmppid = waitpid(childpid, &child_status, child_wait_option)) < 0){
            perror("waitpid");
            Hgoto_error(1);
        }

        /* Close the file  */
        if(H5Fclose(fid) < 0) {
            HDfprintf(stderr, "Failed to close file id\n");
            Hgoto_error(1);
        }

        /* Close the property list */
        if(H5Pclose(fapl) < 0) {
            HDfprintf(stderr, "Failed to close the property list\n");
            Hgoto_error(1);
        }

        if (WIFEXITED(child_status)){
            if ((child_ret_value=WEXITSTATUS(child_status)) != 0){
                HDprintf("%d: child process exited with non-zero code (%d)\n",
                    mypid, child_ret_value);
                Hgoto_error(1);
            }
        } else {
            HDprintf("%d: child process terminated abnormally\n", mypid);
            Hgoto_error(2);
        }
    }
    
done:
    /* Print result and exit */
    if (ret_value != 0){
        HDprintf("Error(s) encountered\n");
    }else{
        HDprintf("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 */

