mirror of
https://github.com/HDFGroup/hdf5.git
synced 2024-11-27 02:10:55 +08:00
3e61010340
* Expand ID dec_ref and close callbacks to allow for asynchronous close operations. * Fix typo * Rename token -> request, remove programmer name * H5E_ATOM to H5E_ID
887 lines
26 KiB
C
887 lines
26 KiB
C
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Copyright by The HDF Group. *
|
|
* Copyright by the Board of Trustees of the University of Illinois. *
|
|
* 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. *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
/* Test user-created identifiers (hid_t's) and identifier types. */
|
|
|
|
#include "testhdf5.h"
|
|
|
|
/* Include H5Ipkg.h to calculate max number of groups */
|
|
#define H5I_FRIEND /*suppress error about including H5Ipkg */
|
|
#include "H5Ipkg.h"
|
|
|
|
static herr_t
|
|
free_wrapper(void *p, void **_ctx)
|
|
{
|
|
HDfree(p);
|
|
return SUCCEED;
|
|
}
|
|
|
|
/* Test basic functionality of registering and deleting types and IDs */
|
|
static int
|
|
basic_id_test(void)
|
|
{
|
|
H5I_type_t myType = H5I_BADID;
|
|
hid_t arrayID = H5I_INVALID_HID;
|
|
void * testObj = NULL;
|
|
void * testPtr = NULL;
|
|
char nameString[10];
|
|
hid_t testID;
|
|
ssize_t testSize = -1;
|
|
herr_t err;
|
|
int num_ref;
|
|
hsize_t num_members;
|
|
|
|
/* Try to register an ID with ficticious types */
|
|
H5E_BEGIN_TRY
|
|
arrayID = H5Iregister((H5I_type_t)420, testObj);
|
|
H5E_END_TRY
|
|
|
|
VERIFY(arrayID, H5I_INVALID_HID, "H5Iregister");
|
|
if (arrayID != H5I_INVALID_HID)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
arrayID = H5Iregister((H5I_type_t)-1, testObj);
|
|
H5E_END_TRY
|
|
|
|
VERIFY(arrayID, H5I_INVALID_HID, "H5Iregister");
|
|
if (arrayID != H5I_INVALID_HID)
|
|
goto out;
|
|
|
|
/* Try to access IDs with ficticious types */
|
|
H5E_BEGIN_TRY
|
|
testPtr = H5Iobject_verify((hid_t)100, (H5I_type_t)0);
|
|
H5E_END_TRY
|
|
|
|
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
|
|
if (testPtr != NULL)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testPtr = H5Iobject_verify((hid_t)700, (H5I_type_t)700);
|
|
H5E_END_TRY
|
|
|
|
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
|
|
if (testPtr != NULL)
|
|
goto out;
|
|
|
|
/* Register a type */
|
|
myType = H5Iregister_type((size_t)64, 0, free_wrapper);
|
|
|
|
CHECK(myType, H5I_BADID, "H5Iregister_type");
|
|
if (myType == H5I_BADID)
|
|
goto out;
|
|
|
|
/* Register an ID and retrieve the object it points to.
|
|
* Once the ID has been registered, testObj will be freed when
|
|
* its ID type is destroyed.
|
|
*/
|
|
testObj = HDmalloc(7 * sizeof(int));
|
|
arrayID = H5Iregister(myType, testObj);
|
|
|
|
CHECK(arrayID, H5I_INVALID_HID, "H5Iregister");
|
|
if (arrayID == H5I_INVALID_HID) {
|
|
HDfree(testObj);
|
|
goto out;
|
|
}
|
|
|
|
testPtr = (int *)H5Iobject_verify(arrayID, myType);
|
|
|
|
CHECK_PTR_EQ(testPtr, testObj, "H5Iobject_verify");
|
|
if (testPtr != testObj)
|
|
goto out;
|
|
|
|
/* Ensure that H5Iget_file_id and H5Iget_name() fail, since this
|
|
* is an hid_t for the wrong kind of object
|
|
*/
|
|
H5E_BEGIN_TRY
|
|
testID = H5Iget_file_id(arrayID);
|
|
H5E_END_TRY
|
|
|
|
VERIFY(testID, H5I_INVALID_HID, "H5Iget_file_id");
|
|
if (testID != H5I_INVALID_HID)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testSize = H5Iget_name(arrayID, nameString, (size_t)9);
|
|
H5E_END_TRY
|
|
|
|
VERIFY(testSize, -1, "H5Iget_name");
|
|
if (testSize != -1)
|
|
goto out;
|
|
|
|
/* Make sure H5Iremove_verify catches objects of the wrong type */
|
|
H5E_BEGIN_TRY
|
|
testPtr = (int *)H5Iremove_verify(arrayID, (H5I_type_t)0);
|
|
H5E_END_TRY
|
|
|
|
CHECK_PTR_NULL(testPtr, "H5Iremove_verify");
|
|
if (testPtr != NULL)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testPtr = (int *)H5Iremove_verify(arrayID, (H5I_type_t)((int)myType - 1));
|
|
H5E_END_TRY
|
|
|
|
CHECK_PTR_NULL(testPtr, "H5Iremove_verify");
|
|
if (testPtr != NULL)
|
|
goto out;
|
|
|
|
/* Remove an ID and make sure we can't access it */
|
|
testPtr = (int *)H5Iremove_verify(arrayID, myType);
|
|
|
|
CHECK_PTR(testPtr, "H5Iremove_verify");
|
|
if (testPtr == NULL)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testPtr = (int *)H5Iobject_verify(arrayID, myType);
|
|
H5E_END_TRY
|
|
|
|
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
|
|
if (testPtr != NULL)
|
|
goto out;
|
|
|
|
/* Delete the type and make sure we can't access objects within it */
|
|
arrayID = H5Iregister(myType, testObj);
|
|
|
|
err = H5Idestroy_type(myType);
|
|
VERIFY(err, 0, "H5Idestroy_type");
|
|
if (err != 0)
|
|
goto out;
|
|
VERIFY(H5Itype_exists(myType), 0, "H5Itype_exists");
|
|
if (H5Itype_exists(myType) != 0)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
VERIFY(H5Inmembers(myType, NULL), -1, "H5Inmembers");
|
|
if (H5Inmembers(myType, NULL) != -1)
|
|
goto out;
|
|
H5E_END_TRY
|
|
|
|
/* Register another type and another object in that type */
|
|
myType = H5Iregister_type((size_t)64, 0, free_wrapper);
|
|
|
|
CHECK(myType, H5I_BADID, "H5Iregister_type");
|
|
if (myType == H5I_BADID)
|
|
goto out;
|
|
|
|
/* The memory that testObj pointed to should already have been
|
|
* freed when the previous type was destroyed. Allocate new
|
|
* memory for it.
|
|
*/
|
|
testObj = HDmalloc(7 * sizeof(int));
|
|
arrayID = H5Iregister(myType, testObj);
|
|
|
|
CHECK(arrayID, H5I_INVALID_HID, "H5Iregister");
|
|
if (arrayID == H5I_INVALID_HID) {
|
|
HDfree(testObj);
|
|
goto out;
|
|
}
|
|
|
|
err = H5Inmembers(myType, &num_members);
|
|
CHECK(err, -1, "H5Inmembers");
|
|
if (err < 0)
|
|
goto out;
|
|
VERIFY(num_members, 1, "H5Inmembers");
|
|
if (num_members != 1)
|
|
goto out;
|
|
|
|
/* Increment references to type and ensure that dec_type_ref
|
|
* doesn't destroy the type
|
|
*/
|
|
num_ref = H5Iinc_type_ref(myType);
|
|
VERIFY(num_ref, 2, "H5Iinc_type_ref");
|
|
if (num_ref != 2)
|
|
goto out;
|
|
num_ref = H5Idec_type_ref(myType);
|
|
VERIFY(num_ref, 1, "H5Idec_type_ref");
|
|
if (num_ref != 1)
|
|
goto out;
|
|
err = H5Inmembers(myType, &num_members);
|
|
CHECK(err, -1, "H5Inmembers");
|
|
if (err < 0)
|
|
goto out;
|
|
VERIFY(num_members, 1, "H5Inmembers");
|
|
if (num_members != 1)
|
|
goto out;
|
|
|
|
/* This call to dec_type_ref should destroy the type */
|
|
num_ref = H5Idec_type_ref(myType);
|
|
VERIFY(num_ref, 0, "H5Idec_type_ref");
|
|
if (num_ref != 0)
|
|
goto out;
|
|
VERIFY(H5Itype_exists(myType), 0, "H5Itype_exists");
|
|
if (H5Itype_exists(myType) != 0)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
err = H5Inmembers(myType, &num_members);
|
|
if (err >= 0)
|
|
goto out;
|
|
H5E_END_TRY
|
|
|
|
return 0;
|
|
|
|
out:
|
|
/* Clean up type if it has been allocated and free memory used
|
|
* by testObj
|
|
*/
|
|
if (myType >= 0)
|
|
H5Idestroy_type(myType);
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* A dummy search function for the next test */
|
|
static int
|
|
test_search_func(void H5_ATTR_UNUSED *ptr1, hid_t H5_ATTR_UNUSED id, void H5_ATTR_UNUSED *ptr2)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Ensure that public functions cannot access "predefined" ID types */
|
|
static int
|
|
id_predefined_test(void)
|
|
{
|
|
void * testObj;
|
|
hid_t testID;
|
|
hid_t typeID = H5I_INVALID_HID;
|
|
void * testPtr;
|
|
herr_t testErr;
|
|
|
|
testObj = HDmalloc(sizeof(int));
|
|
|
|
/*
|
|
* Attempt to perform public functions on various library types
|
|
*/
|
|
|
|
H5E_BEGIN_TRY
|
|
testID = H5Iregister(H5I_FILE, testObj);
|
|
H5E_END_TRY
|
|
|
|
VERIFY(testID, H5I_INVALID_HID, "H5Iregister");
|
|
if (testID != H5I_INVALID_HID)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testPtr = H5Isearch(H5I_GENPROP_LST, test_search_func, testObj);
|
|
H5E_END_TRY
|
|
|
|
CHECK_PTR_NULL(testPtr, "H5Isearch");
|
|
if (testPtr != NULL)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testErr = H5Inmembers(H5I_ERROR_STACK, NULL);
|
|
H5E_END_TRY
|
|
|
|
VERIFY(testErr, -1, "H5Inmembers");
|
|
if (testErr != -1)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testErr = H5Iclear_type(H5I_FILE, 0);
|
|
H5E_END_TRY
|
|
|
|
VERIFY((testErr >= 0), 0, "H5Iclear_type");
|
|
if (testErr >= 0)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testErr = H5Idestroy_type(H5I_DATASET);
|
|
H5E_END_TRY
|
|
|
|
VERIFY((testErr >= 0), 0, "H5Idestroy_type");
|
|
if (testErr >= 0)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testErr = H5Itype_exists(H5I_GROUP);
|
|
H5E_END_TRY
|
|
|
|
VERIFY(testErr, -1, "H5Itype_exists");
|
|
if (testErr != -1)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testErr = H5Itype_exists(H5I_ATTR);
|
|
H5E_END_TRY
|
|
|
|
VERIFY(testErr, -1, "H5Itype_exists");
|
|
if (testErr != -1)
|
|
goto out;
|
|
|
|
/*
|
|
* Create a datatype ID and try to perform illegal functions on it
|
|
*/
|
|
|
|
typeID = H5Tcreate(H5T_OPAQUE, (size_t)42);
|
|
CHECK(typeID, H5I_INVALID_HID, "H5Tcreate");
|
|
if (typeID == H5I_INVALID_HID)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testPtr = H5Iremove_verify(typeID, H5I_DATATYPE);
|
|
H5E_END_TRY
|
|
|
|
CHECK_PTR_NULL(testPtr, "H5Iremove_verify");
|
|
if (testPtr != NULL)
|
|
goto out;
|
|
|
|
H5E_BEGIN_TRY
|
|
testPtr = H5Iobject_verify(typeID, H5I_DATATYPE);
|
|
H5E_END_TRY
|
|
|
|
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
|
|
if (testPtr != NULL)
|
|
goto out;
|
|
|
|
H5Tclose(typeID);
|
|
|
|
/* testObj was never registered as an atom, so it will not be
|
|
* automatically freed. */
|
|
HDfree(testObj);
|
|
return 0;
|
|
|
|
out:
|
|
if (typeID != H5I_INVALID_HID)
|
|
H5Tclose(typeID);
|
|
if (testObj != NULL)
|
|
HDfree(testObj);
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Test the H5Iis_valid function */
|
|
static int
|
|
test_is_valid(void)
|
|
{
|
|
hid_t dtype; /* datatype id */
|
|
int64_t nmembs1; /* number of type memnbers */
|
|
int64_t nmembs2;
|
|
htri_t tri_ret; /* htri_t return value */
|
|
herr_t ret; /* return value */
|
|
|
|
/* Create a datatype id */
|
|
dtype = H5Tcopy(H5T_NATIVE_INT);
|
|
CHECK(dtype, FAIL, "H5Tcopy");
|
|
if (dtype < 0)
|
|
goto out;
|
|
|
|
/* Check that the ID is valid */
|
|
tri_ret = H5Iis_valid(dtype);
|
|
VERIFY(tri_ret, TRUE, "H5Iis_valid");
|
|
if (tri_ret != TRUE)
|
|
goto out;
|
|
|
|
/* Artificially manipulate the reference counts so app_count is 0, and dtype
|
|
* appears to be an internal id. This takes advantage of the fact that
|
|
* H5Ipkg is included.
|
|
*/
|
|
ret = H5I_inc_ref(dtype, FALSE);
|
|
CHECK(ret, FAIL, "H5I_inc_ref");
|
|
if (ret < 0)
|
|
goto out;
|
|
ret = H5I_dec_app_ref(dtype);
|
|
CHECK(ret, FAIL, "H5I_dec_ref");
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
/* Check that dtype is invalid */
|
|
tri_ret = H5Iis_valid(dtype);
|
|
VERIFY(tri_ret, FALSE, "H5Iis_valid");
|
|
if (tri_ret != FALSE)
|
|
goto out;
|
|
|
|
/* Close dtype and verify that it has been closed */
|
|
nmembs1 = H5I_nmembers(H5I_DATATYPE);
|
|
CHECK(nmembs1, FAIL, "H5I_nmembers");
|
|
if (nmembs1 < 0)
|
|
goto out;
|
|
ret = H5I_dec_ref(dtype);
|
|
CHECK(ret, FAIL, "H5I_dec_ref");
|
|
if (ret < 0)
|
|
goto out;
|
|
nmembs2 = H5I_nmembers(H5I_DATATYPE);
|
|
VERIFY(nmembs2, nmembs1 - 1, "H5I_nmembers");
|
|
if (nmembs2 != nmembs1 - 1)
|
|
goto out;
|
|
|
|
/* Check that dtype is invalid */
|
|
tri_ret = H5Iis_valid(dtype);
|
|
VERIFY(tri_ret, FALSE, "H5Iis_valid");
|
|
if (tri_ret != FALSE)
|
|
goto out;
|
|
|
|
/* Check that an id of -1 is invalid */
|
|
tri_ret = H5Iis_valid((hid_t)-1);
|
|
VERIFY(tri_ret, FALSE, "H4Iis_valid");
|
|
if (tri_ret != FALSE)
|
|
goto out;
|
|
|
|
return 0;
|
|
|
|
out:
|
|
/* Don't attempt to close dtype as we don't know the exact state of the
|
|
* reference counts. Every state in this function will be automatically
|
|
* closed at library exit anyways, as internal count is never > 1.
|
|
*/
|
|
return -1;
|
|
}
|
|
|
|
/* Test the H5Iget_type function */
|
|
static int
|
|
test_get_type(void)
|
|
{
|
|
hid_t dtype; /* datatype id */
|
|
H5I_type_t type_ret; /* return value */
|
|
|
|
/* Create a datatype id */
|
|
dtype = H5Tcopy(H5T_NATIVE_INT);
|
|
CHECK(dtype, FAIL, "H5Tcopy");
|
|
if (dtype < 0)
|
|
goto out;
|
|
|
|
/* Check that the ID is correct */
|
|
type_ret = H5Iget_type(dtype);
|
|
VERIFY(type_ret, H5I_DATATYPE, "H5Iget_type");
|
|
if (type_ret == H5I_BADID)
|
|
goto out;
|
|
|
|
/* Check that the ID is correct */
|
|
type_ret = H5Iget_type((hid_t)H5T_STRING);
|
|
VERIFY(type_ret, H5I_BADID, "H5Iget_type");
|
|
if (type_ret != H5I_BADID)
|
|
goto out;
|
|
|
|
/* Check that the ID is correct */
|
|
type_ret = H5Iget_type((hid_t)-1);
|
|
VERIFY(type_ret, H5I_BADID, "H5Iget_type");
|
|
if (type_ret != H5I_BADID)
|
|
goto out;
|
|
|
|
H5Tclose(dtype);
|
|
|
|
return 0;
|
|
|
|
out:
|
|
if (dtype != H5I_INVALID_HID)
|
|
H5Tclose(dtype);
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Test boundary cases with lots of types */
|
|
|
|
/* Type IDs range from H5I_NTYPES to H5I_MAX_NUM_TYPES. The system will assign */
|
|
/* IDs in sequential order until H5I_MAX_NUM_TYPES IDs have been given out, at which */
|
|
/* point it will search for type IDs that were allocated but have since been */
|
|
/* deleted. */
|
|
/* This test will allocate IDs up to H5I_MAX_NUM_TYPES, ensure that IDs wrap around */
|
|
/* to low values successfully, ensure that an error is thrown when all possible */
|
|
/* type IDs are taken, then ensure that deleting types frees up their IDs. */
|
|
/* Note that this test depends on the implementation of IDs, so may break */
|
|
/* if the implementation changes. */
|
|
/* Also note that if someone else registered a user-defined type and forgot to */
|
|
/* destroy it, this test will mysteriously fail (because it will expect there to */
|
|
/* be one more "free" type ID than there is). */
|
|
/* H5I_NTYPES is defined in h5public.h, H5I_MAX_NUM_TYPES is defined in h5pkg.h */
|
|
static int
|
|
test_id_type_list(void)
|
|
{
|
|
H5I_type_t startType; /* The first type ID we were assigned in this test */
|
|
H5I_type_t currentType;
|
|
H5I_type_t testType;
|
|
int i; /* Just a counter variable */
|
|
|
|
startType = H5Iregister_type((size_t)8, 0, free_wrapper);
|
|
CHECK(startType, H5I_BADID, "H5Iregister_type");
|
|
if (startType == H5I_BADID)
|
|
goto out;
|
|
|
|
/* Sanity check */
|
|
if ((int)startType >= H5I_MAX_NUM_TYPES || startType < H5I_NTYPES) {
|
|
/* Error condition, throw an error */
|
|
CHECK(1, 1, "H5Iregister_type");
|
|
goto out;
|
|
}
|
|
/* Create types up to H5I_MAX_NUM_TYPES */
|
|
for (i = startType + 1; i < H5I_MAX_NUM_TYPES; i++) {
|
|
currentType = H5Iregister_type((size_t)8, 0, free_wrapper);
|
|
CHECK(currentType, H5I_BADID, "H5Iregister_type");
|
|
if (currentType == H5I_BADID)
|
|
goto out;
|
|
}
|
|
|
|
/* Wrap around to low type ID numbers */
|
|
for (i = H5I_NTYPES; i < startType; i++) {
|
|
currentType = H5Iregister_type((size_t)8, 0, free_wrapper);
|
|
CHECK(currentType, H5I_BADID, "H5Iregister_type");
|
|
if (currentType == H5I_BADID)
|
|
goto out;
|
|
}
|
|
|
|
/* There should be no room at the inn for a new ID type*/
|
|
H5E_BEGIN_TRY
|
|
testType = H5Iregister_type((size_t)8, 0, free_wrapper);
|
|
H5E_END_TRY
|
|
|
|
VERIFY(testType, H5I_BADID, "H5Iregister_type");
|
|
if (testType != H5I_BADID)
|
|
goto out;
|
|
|
|
/* Now delete a type and try to insert again */
|
|
H5Idestroy_type(H5I_NTYPES);
|
|
testType = H5Iregister_type((size_t)8, 0, free_wrapper);
|
|
|
|
VERIFY(testType, H5I_NTYPES, "H5Iregister_type");
|
|
if (testType != H5I_NTYPES)
|
|
goto out;
|
|
|
|
/* Cleanup. Destroy all types. */
|
|
for (i = H5I_NTYPES; i < H5I_MAX_NUM_TYPES; i++)
|
|
H5Idestroy_type((H5I_type_t)i);
|
|
|
|
return 0;
|
|
|
|
out:
|
|
/* Cleanup. For simplicity, just destroy all types and ignore errors. */
|
|
H5E_BEGIN_TRY
|
|
for (i = H5I_NTYPES; i < H5I_MAX_NUM_TYPES; i++)
|
|
H5Idestroy_type((H5I_type_t)i);
|
|
H5E_END_TRY
|
|
return -1;
|
|
}
|
|
|
|
/* Test removing ids in callback for H5Iclear_type */
|
|
|
|
/* There was a rare bug where, if an id free callback being called by
|
|
* H5I_clear_type() removed another id in that type, a segfault could occur.
|
|
* This test tests for that error (and freeing ids "out of order" within
|
|
* H5Iclear_type() in general).
|
|
*
|
|
* NB: RCT = "remove clear type"
|
|
*/
|
|
|
|
/* Macro definitions */
|
|
#define RCT_MAX_NOBJS 25 /* Maximum number of objects in the list */
|
|
#define RCT_MIN_NOBJS 5
|
|
#define RCT_NITER 50 /* Number of times we cycle through object creation and deletion */
|
|
|
|
/* Structure to hold the master list of objects */
|
|
typedef struct rct_obj_list_t {
|
|
|
|
/* Pointer to the objects */
|
|
struct rct_obj_t *objects;
|
|
|
|
/* The number of objects in the list */
|
|
long count;
|
|
|
|
/* The number of objects in the list that have not been freed */
|
|
long remaining;
|
|
} rct_obj_list_t;
|
|
|
|
/* Structure for an object */
|
|
typedef struct rct_obj_t {
|
|
/* The ID for this object */
|
|
hid_t id;
|
|
|
|
/* The number of times this object has been freed */
|
|
int nfrees;
|
|
|
|
/* Whether we are currently freeing this object directly
|
|
* through H5Idec_ref().
|
|
*/
|
|
hbool_t freeing;
|
|
|
|
/* Pointer to the master list of all objects */
|
|
rct_obj_list_t *list;
|
|
} rct_obj_t;
|
|
|
|
/* Free callback passed to H5Iclear_type()
|
|
*
|
|
* When invoked on a closing object, frees a random unfreed ID in the
|
|
* master list of objects.
|
|
*/
|
|
static herr_t
|
|
rct_free_cb(void *_obj, void **_ctx)
|
|
{
|
|
rct_obj_t * obj = (rct_obj_t *)_obj;
|
|
long remove_nth;
|
|
long i;
|
|
herr_t ret;
|
|
|
|
/* Mark this object as freed */
|
|
obj->nfrees++;
|
|
|
|
/* Decrement the number of objects in the list that have not been freed */
|
|
obj->list->remaining--;
|
|
|
|
/* If this object isn't already being freed by a callback free call and
|
|
* the master object list still contains objects to free, pick another
|
|
* object and free it.
|
|
*/
|
|
if (!obj->freeing && (obj->list->remaining > 0)) {
|
|
|
|
/* Pick a random object from the list. This is done by picking a
|
|
* random number between 0 and the # of remaining unfreed objects
|
|
* and then scanning through the list to find that nth unfreed
|
|
* object.
|
|
*/
|
|
remove_nth = HDrandom() % obj->list->remaining;
|
|
for (i = 0; i < obj->list->count; i++)
|
|
if (obj->list->objects[i].nfrees == 0) {
|
|
if (remove_nth == 0)
|
|
break;
|
|
else
|
|
remove_nth--;
|
|
}
|
|
|
|
/* Badness if we scanned through the list and didn't manage to
|
|
* select one to delete (the list stats were probably updated
|
|
* incorrectly).
|
|
*/
|
|
if (i == obj->list->count) {
|
|
ERROR("invalid obj_list");
|
|
goto error;
|
|
}
|
|
|
|
/* Mark the object we're about to free so its own callback does
|
|
* not free another object. We don't want to recursively free the
|
|
* entire list when we free the first ID.
|
|
*/
|
|
obj->list->objects[i].freeing = TRUE;
|
|
|
|
/* Decrement the reference count on the object */
|
|
ret = H5Idec_ref(obj->list->objects[i].id);
|
|
CHECK(ret, FAIL, "H5Idec_ref");
|
|
if (ret == FAIL)
|
|
goto error;
|
|
|
|
/* Unset the "freeing" flag */
|
|
obj->list->objects[i].freeing = FALSE;
|
|
}
|
|
|
|
/* Verify the number of objects remaining in the master list is non-negative */
|
|
if (obj->list->remaining < 0) {
|
|
ERROR("invalid number of objects remaining");
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
return -1;
|
|
} /* end rct_free_cb() */
|
|
|
|
/* Test function */
|
|
static int
|
|
test_remove_clear_type(void)
|
|
{
|
|
H5I_type_t obj_type;
|
|
rct_obj_list_t obj_list;
|
|
rct_obj_t * objects = NULL; /* Convenience pointer to objects stored in master list */
|
|
size_t list_size;
|
|
long i, j;
|
|
herr_t ret; /* return value */
|
|
|
|
/* Register a user-defined type with our custom ID-deleting callback */
|
|
obj_type = H5Iregister_type((size_t)8, 0, rct_free_cb);
|
|
CHECK(obj_type, H5I_BADID, "H5Iregister_type");
|
|
if (obj_type == H5I_BADID)
|
|
goto error;
|
|
|
|
/* Create an array to hold the objects in the master list */
|
|
list_size = RCT_MAX_NOBJS * sizeof(rct_obj_t);
|
|
obj_list.objects = HDmalloc(list_size);
|
|
CHECK(obj_list.objects, NULL, "HDcalloc");
|
|
if (NULL == obj_list.objects)
|
|
goto error;
|
|
|
|
/* Set a convenience pointer to the object array */
|
|
objects = obj_list.objects;
|
|
|
|
for (i = 0; i < RCT_NITER; i++) {
|
|
|
|
/* The number of members in the type, according to the HDF5 library */
|
|
hsize_t nmembers = 1234567;
|
|
|
|
/* The number of objects found while scanning through the object list */
|
|
unsigned found;
|
|
|
|
/*********************
|
|
* Build object list *
|
|
*********************/
|
|
|
|
HDmemset(obj_list.objects, 0, list_size);
|
|
|
|
/* The number of objects used is a random number between the min and max */
|
|
obj_list.count = obj_list.remaining =
|
|
RCT_MIN_NOBJS + (HDrandom() % (long)(RCT_MAX_NOBJS - RCT_MIN_NOBJS + 1));
|
|
|
|
/* Create the actual objects */
|
|
for (j = 0; j < obj_list.count; j++) {
|
|
|
|
/* Object setup */
|
|
objects[j].nfrees = 0;
|
|
objects[j].freeing = FALSE;
|
|
objects[j].list = &obj_list;
|
|
|
|
/* Register an ID for it */
|
|
objects[j].id = H5Iregister(obj_type, &objects[j]);
|
|
CHECK(objects[j].id, FAIL, "H5Iregister");
|
|
if (objects[j].id == FAIL)
|
|
goto error;
|
|
|
|
/* Bump the reference count by 1 (to 2) 50% of the time */
|
|
if (HDrandom() % 2) {
|
|
ret = H5Iinc_ref(objects[j].id);
|
|
CHECK(ret, FAIL, "H5Iinc_ref");
|
|
if (ret == FAIL)
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/******************************************
|
|
* Clear the type with force set to FALSE *
|
|
******************************************/
|
|
|
|
/* Clear the type. Since force is FALSE, only
|
|
* IDs with a reference count of 1 will be cleared.
|
|
*/
|
|
ret = H5Iclear_type(obj_type, FALSE);
|
|
CHECK(ret, FAIL, "H5Iclear_type");
|
|
if (ret == FAIL)
|
|
goto error;
|
|
|
|
/* Verify that the object struct fields are sane and count the
|
|
* number of unfreed objects
|
|
*/
|
|
found = 0;
|
|
for (j = 0; j < obj_list.count; j++) {
|
|
|
|
if (objects[j].nfrees == 0) {
|
|
/* Count unfreed objects */
|
|
found++;
|
|
}
|
|
else {
|
|
/* Every freed object should have been freed exactly once */
|
|
VERIFY(objects[j].nfrees, 1, "object freed more than once");
|
|
if (objects[j].nfrees != 1)
|
|
goto error;
|
|
}
|
|
|
|
/* No object should still be marked as "freeing" */
|
|
VERIFY(objects[j].freeing, FALSE, "object marked as freeing");
|
|
if (objects[j].freeing != FALSE)
|
|
goto error;
|
|
}
|
|
|
|
/* Verify the number of unfreed objects we found during our scan
|
|
* matches the number stored in the list
|
|
*/
|
|
VERIFY(obj_list.remaining, found, "incorrect number of objects remaining");
|
|
if (obj_list.remaining != found)
|
|
goto error;
|
|
|
|
/* Make sure the HDF5 library confirms our count */
|
|
ret = H5Inmembers(obj_type, &nmembers);
|
|
CHECK(ret, FAIL, "H5Inmembers");
|
|
if (ret == FAIL)
|
|
goto error;
|
|
VERIFY(nmembers, found, "The number of members remaining in the type did not match our count");
|
|
if (nmembers != found)
|
|
goto error;
|
|
|
|
/*****************************************
|
|
* Clear the type with force set to TRUE *
|
|
*****************************************/
|
|
|
|
/* Clear the type. Since force is TRUE, all IDs will be cleared. */
|
|
ret = H5Iclear_type(obj_type, TRUE);
|
|
CHECK(ret, FAIL, "H5Iclear_type");
|
|
if (ret == FAIL)
|
|
goto error;
|
|
|
|
/* Verify that the object struct fields are sane */
|
|
for (j = 0; j < obj_list.count; j++) {
|
|
|
|
/* Every object should have been freed exactly once */
|
|
VERIFY(objects[j].nfrees, 1, "object freed more than once");
|
|
if (objects[j].nfrees != 1)
|
|
goto error;
|
|
|
|
/* No object should still be marked as "freeing" */
|
|
VERIFY(objects[j].freeing, FALSE, "object marked as freeing");
|
|
if (objects[j].freeing != FALSE)
|
|
goto error;
|
|
}
|
|
|
|
/* Verify the number of objects is 0 */
|
|
VERIFY(obj_list.remaining, 0, "objects remaining was not zero");
|
|
if (obj_list.remaining != 0)
|
|
goto error;
|
|
|
|
/* Make sure the HDF5 library confirms zero members in the type */
|
|
ret = H5Inmembers(obj_type, &nmembers);
|
|
CHECK(ret, FAIL, "H5Inmembers");
|
|
if (ret == FAIL)
|
|
goto error;
|
|
VERIFY(nmembers, 0, "The number of members remaining in the type was not zero");
|
|
if (nmembers != 0)
|
|
goto error;
|
|
}
|
|
|
|
/* Destroy the type */
|
|
ret = H5Idestroy_type(obj_type);
|
|
CHECK(ret, FAIL, "H5Idestroy_type");
|
|
if (ret == FAIL)
|
|
goto error;
|
|
|
|
/* Free the object array */
|
|
HDfree(obj_list.objects);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
/* Cleanup. For simplicity, just destroy the types and ignore errors. */
|
|
H5E_BEGIN_TRY {
|
|
H5Idestroy_type(obj_type);
|
|
} H5E_END_TRY
|
|
|
|
HDfree(obj_list.objects);
|
|
|
|
return -1;
|
|
} /* end test_remove_clear_type() */
|
|
|
|
void
|
|
test_ids(void)
|
|
{
|
|
/* Set the random # seed */
|
|
HDsrandom((unsigned)HDtime(NULL));
|
|
|
|
if (basic_id_test() < 0)
|
|
TestErrPrintf("Basic ID test failed\n");
|
|
if (id_predefined_test() < 0)
|
|
TestErrPrintf("Predefined ID type test failed\n");
|
|
if (test_is_valid() < 0)
|
|
TestErrPrintf("H5Iis_valid test failed\n");
|
|
if (test_get_type() < 0)
|
|
TestErrPrintf("H5Iget_type test failed\n");
|
|
if (test_id_type_list() < 0)
|
|
TestErrPrintf("ID type list test failed\n");
|
|
if (test_remove_clear_type() < 0)
|
|
TestErrPrintf("ID remove during H5Iclear_type test failed\n");
|
|
}
|