/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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 LICENSE file, which can be found at the root of the source code       *
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/********************************************************************
 *
 * Testing for thread safety in H5A library operations.
 * ------------------------------------------------------------------
 *
 * Purpose: Verify that the segmentation fault described in HDFFV-11080
 *          is fixed.
 *
 *          This test simulates what the user did to trigger the error:
 *          --Create an HDF5 file
 *          --Create an attribute with variable length string datatype
 *          --Attach the attribute to a group
 *          --Write data to the attribute
 *          --Close the file
 *          --Create NUM_THREADS threads
 *          --For each thread:
 *              --Open the test file
 *              --Open and read the attribute for each opened file
 *
 *          The cause of the problem in this jira issue is due to the file pointer
 *          that is set in the variable length string datatype for the attribute.
 *          That file pointer is already closed and therefore needs to be set to
 *          the current opened file pointer when the attribute is accessed.
 *          Similar patch up was done before when reading dataset in H5D__read()
 *          in src/H5Aint.c.
 *          Hopefully this kind of patch can go away when we resolve the
 *          shared file pointer issue.
 *
 ********************************************************************/

#include "ttsafe.h"

#ifdef H5_HAVE_THREADSAFE

#define FILENAME    "ttsafe_attr_vlen.h5"
#define ATTR_NAME   "root_attr"
#define NUM_THREADS 32

H5TS_THREAD_RETURN_TYPE tts_attr_vlen_thread(void *);

void
tts_attr_vlen(const void H5_ATTR_UNUSED *params)
{
    H5TS_thread_t threads[NUM_THREADS] = {0};             /* Thread declaration */
    hid_t         fid                  = H5I_INVALID_HID; /* File ID */
    hid_t         gid                  = H5I_INVALID_HID; /* Group ID */
    hid_t         atid                 = H5I_INVALID_HID; /* Datatype ID for attribute */
    hid_t         asid                 = H5I_INVALID_HID; /* Dataspace ID for attribute */
    hid_t         aid                  = H5I_INVALID_HID; /* The attribute ID */
    const char   *string_attr          = "2.0";           /* The attribute data */
    int           ret;                                    /* Return value */
    int           i;                                      /* Local index variable */

    /* Create the HDF5 test file */
    fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
    CHECK(fid, H5I_INVALID_HID, "H5Fcreate");

    /* Create variable length string type for attribute */
    atid = H5Tcopy(H5T_C_S1);
    CHECK(atid, H5I_INVALID_HID, "H5Tcopy");
    H5Tset_size(atid, H5T_VARIABLE);

    /* Create dataspace for attribute */
    asid = H5Screate(H5S_SCALAR);
    CHECK(asid, H5I_INVALID_HID, "H5Screate");

    /* Open the root group */
    gid = H5Gopen2(fid, "/", H5P_DEFAULT);
    CHECK(gid, H5I_INVALID_HID, "H5Gopen2");

    /* Attach the attribute to the root group */
    aid = H5Acreate2(gid, ATTR_NAME, atid, asid, H5P_DEFAULT, H5P_DEFAULT);
    CHECK(aid, H5I_INVALID_HID, "H5Acreate2");

    /* Write data to the attribute */
    ret = H5Awrite(aid, atid, &string_attr);
    CHECK(ret, H5I_INVALID_HID, "H5Awrite");

    /* Close IDs */
    ret = H5Sclose(asid);
    CHECK(ret, H5I_INVALID_HID, "H5Sclose");

    ret = H5Aclose(aid);
    CHECK(ret, H5I_INVALID_HID, "H5Aclose");

    ret = H5Gclose(gid);
    CHECK(ret, H5I_INVALID_HID, "H5Gclose");

    ret = H5Fclose(fid);
    CHECK(ret, H5I_INVALID_HID, "H5Fclose");

    ret = H5Tclose(atid);
    CHECK(ret, H5I_INVALID_HID, "H5Tclose");

    /* Start multiple threads and execute tts_attr_vlen_thread() for each thread */
    for (i = 0; i < NUM_THREADS; i++)
        if (H5TS_thread_create(&threads[i], tts_attr_vlen_thread, NULL) < 0)
            TestErrPrintf("thread # %d did not start", i);

    /* Wait for the threads to end */
    for (i = 0; i < NUM_THREADS; i++)
        if (H5TS_thread_join(threads[i], NULL) < 0)
            TestErrPrintf("thread %d failed to join", i);

} /* end tts_attr_vlen() */

/* Start execution for each thread */
H5TS_THREAD_RETURN_TYPE
tts_attr_vlen_thread(void H5_ATTR_UNUSED *client_data)
{
    hid_t       fid  = H5I_INVALID_HID; /* File ID */
    hid_t       gid  = H5I_INVALID_HID; /* Group ID */
    hid_t       aid  = H5I_INVALID_HID; /* Attribute ID */
    hid_t       asid = H5I_INVALID_HID; /* Dataspace ID for the attribute */
    hid_t       atid = H5I_INVALID_HID; /* Datatype ID for the attribute */
    char       *string_attr_check;      /* The attribute data being read */
    const char *string_attr = "2.0";    /* The expected attribute data */
    herr_t      ret;                    /* Return value */

    /* Open the test file */
    fid = H5Fopen(FILENAME, H5F_ACC_RDONLY, H5P_DEFAULT);
    CHECK(fid, H5I_INVALID_HID, "H5Fopen");

    /* Open the group */
    gid = H5Gopen2(fid, "/", H5P_DEFAULT);
    CHECK(gid, H5I_INVALID_HID, "H5Gopen");

    /* Open the attribute */
    aid = H5Aopen(gid, "root_attr", H5P_DEFAULT);
    CHECK(aid, H5I_INVALID_HID, "H5Aopen");

    /* Get the attribute datatype */
    atid = H5Aget_type(aid);
    CHECK(atid, H5I_INVALID_HID, "H5Aget_type");

    /* Get the dataspace for the attribute */
    asid = H5Aget_space(aid);
    CHECK(asid, H5I_INVALID_HID, "H5Aget_space");

    /* Read the attribute */
    ret = H5Aread(aid, atid, &string_attr_check);
    CHECK(ret, FAIL, "H5Aclose");

    /* Verify the attribute data is as expected */
    VERIFY_STR(string_attr_check, string_attr, "H5Aread");

    /* Free the attribute data */
    ret = H5Treclaim(atid, asid, H5P_DEFAULT, &string_attr_check);
    CHECK(ret, FAIL, "H5Dvlen_reclaim");

    /* Close IDs */
    ret = H5Aclose(aid);
    CHECK(ret, FAIL, "H5Aclose");

    ret = H5Sclose(asid);
    CHECK(ret, FAIL, "H5Aclose");

    ret = H5Gclose(gid);
    CHECK(ret, FAIL, "H5Aclose");

    ret = H5Fclose(fid);
    CHECK(ret, FAIL, "H5Aclose");

    ret = H5Tclose(atid);
    CHECK(ret, FAIL, "H5Aclose");

    return (H5TS_thread_ret_t)0;
} /* end tts_attr_vlen_thread() */

void
cleanup_attr_vlen(void H5_ATTR_UNUSED *params)
{
    if (GetTestCleanup()) {
        HDunlink(FILENAME);
    }
}

#endif /*H5_HAVE_THREADSAFE*/
