diff --git a/fortran/src/H5Fff.F90 b/fortran/src/H5Fff.F90 index fad1ff8a993..a8055934a56 100644 --- a/fortran/src/H5Fff.F90 +++ b/fortran/src/H5Fff.F90 @@ -41,7 +41,13 @@ MODULE H5F IMPLICIT NONE ! Number of objects opened in H5open_f - INTEGER(SIZE_T) :: H5OPEN_NUM_OBJ + INTEGER(SIZE_T) :: H5OPEN_NUM_OBJ = 0 + ! The same count broken down by object type, in the order of the object type + ! list in h5fget_obj_count_f, so that a count can be adjusted without assuming + ! which types h5open_f creates. + INTEGER(SIZE_T), DIMENSION(1:4) :: H5OPEN_NUM_OBJ_BY_TYPE = 0 + + PRIVATE :: h5open_num_obj_matching, h5open_owns_id #ifndef H5_DOXYGEN @@ -863,21 +869,55 @@ CONTAINS END SUBROUTINE h5fclose_async_f + ! Number of the objects h5open_f left open that a count or list of obj_type + ! would include. + FUNCTION h5open_num_obj_matching(obj_type) RESULT(num_obj) + IMPLICIT NONE + INTEGER, INTENT(IN) :: obj_type + INTEGER(SIZE_T) :: num_obj + + IF(obj_type.EQ.H5F_OBJ_ALL_F)THEN + num_obj = H5OPEN_NUM_OBJ + ELSE + num_obj = 0 + IF(IAND(obj_type,H5F_OBJ_FILE_F ).NE.0) num_obj = num_obj + H5OPEN_NUM_OBJ_BY_TYPE(1) + IF(IAND(obj_type,H5F_OBJ_DATASET_F ).NE.0) num_obj = num_obj + H5OPEN_NUM_OBJ_BY_TYPE(2) + IF(IAND(obj_type,H5F_OBJ_GROUP_F ).NE.0) num_obj = num_obj + H5OPEN_NUM_OBJ_BY_TYPE(3) + IF(IAND(obj_type,H5F_OBJ_DATATYPE_F).NE.0) num_obj = num_obj + H5OPEN_NUM_OBJ_BY_TYPE(4) + ENDIF + + END FUNCTION h5open_num_obj_matching + + ! Whether id is one of the identifiers h5open_f opened for the Fortran interface. + FUNCTION h5open_owns_id(id) RESULT(owns) + IMPLICIT NONE + INTEGER(HID_T), INTENT(IN) :: id + LOGICAL :: owns + + owns = ANY(predef_types.EQ.id) .OR. ANY(floating_types.EQ.id) .OR. ANY(integer_types.EQ.id) + + END FUNCTION h5open_owns_id + !> !! \ingroup FH5F !! !! \brief Gets number of the objects open within a file. !! !! \param file_id File identifier -!! \param obj_type Type of the object; possible values are: +!! \param obj_type Type of the objects to count, either one of the following or +!! several of them combined with IOR(): !! \li H5F_OBJ_FILE_F !! \li H5F_OBJ_DATASET_F !! \li H5F_OBJ_GROUP_F !! \li H5F_OBJ_DATATYPE_F -!! \li H5F_OBJ_ALL_F +!! \li H5F_OBJ_ALL_F, which counts all of the above !! \param obj_count Number of open objects !! \param hdferr \fortran_error !! +!! \note When \p file_id is H5F_OBJ_ALL_F the count excludes the objects that +!! h5open_f opens for the Fortran interface, so it reports only what the +!! application itself has open. +!! !! See C API: @ref H5Fget_obj_count() !! SUBROUTINE h5fget_obj_count_f(file_id, obj_type, obj_count, hdferr) @@ -887,6 +927,8 @@ CONTAINS INTEGER(SIZE_T), INTENT(OUT) :: obj_count INTEGER , INTENT(OUT) :: hdferr + INTEGER(SIZE_T) :: count_loc + INTERFACE INTEGER(SIZE_T) FUNCTION H5Fget_obj_count(file_id, obj_type) BIND(C,NAME='H5Fget_obj_count') IMPORT :: C_INT @@ -897,15 +939,21 @@ CONTAINS END FUNCTION H5Fget_obj_count END INTERFACE - obj_count = H5Fget_obj_count(file_id, INT(obj_type, C_INT)) + count_loc = H5Fget_obj_count(file_id, INT(obj_type, C_INT)) hdferr = 0 - IF(obj_count.LT.0) hdferr = -1 - ! Don't include objects created by H5open in the H5F_OBJ_ALL_F count - IF(file_id.EQ.INT(H5F_OBJ_ALL_F,HID_T))THEN - obj_count = obj_count - H5OPEN_NUM_OBJ - ENDIF + ! H5Fget_obj_count reports failure as a negative count. + IF(count_loc.LT.0) hdferr = -1 + + ! Don't include the objects h5open_f left open. + IF(file_id.EQ.INT(H5F_OBJ_ALL_F,HID_T)) & + count_loc = count_loc - h5open_num_obj_matching(obj_type) + + ! A count is never negative, so neither is one that has been adjusted. + IF(count_loc.LT.0) hdferr = -1 + + obj_count = count_loc END SUBROUTINE h5fget_obj_count_f @@ -915,17 +963,22 @@ CONTAINS !! \brief Get list of open objects identifiers within a file. !! !! \param file_id File identifier -!! \param obj_type Type of the object; possible values are: +!! \param obj_type Type of the objects to list, either one of the following or +!! several of them combined with IOR(): !! \li H5F_OBJ_FILE_F !! \li H5F_OBJ_DATASET_F !! \li H5F_OBJ_GROUP_F !! \li H5F_OBJ_DATATYPE_F -!! \li H5F_OBJ_ALL_F +!! \li H5F_OBJ_ALL_F, which lists all of the above !! \param max_objs Maximum # of objects to retrieve !! \param obj_ids Array of open object identifiers !! \param hdferr \fortran_error !! \param num_objs Number of open objects !! +!! \note When \p file_id is H5F_OBJ_ALL_F the list excludes the identifiers that +!! h5open_f opens for the Fortran interface, matching h5fget_obj_count_f, so +!! it reports only what the application itself has open. +!! !! See C API: @ref H5Fget_obj_ids() !! SUBROUTINE h5fget_obj_ids_f(file_id, obj_type, max_objs, obj_ids, hdferr, num_objs) @@ -938,6 +991,10 @@ CONTAINS INTEGER(SIZE_T), INTENT(OUT), OPTIONAL :: num_objs INTEGER(SIZE_T) :: c_num_objs ! Number of open objects of the specified type + INTEGER(SIZE_T) :: num_skip ! Objects h5open_f left open that this query would return + INTEGER(SIZE_T) :: idx, num_kept + INTEGER :: alloc_stat + INTEGER(HID_T), ALLOCATABLE :: obj_ids_loc(:) INTERFACE INTEGER(SIZE_T) FUNCTION H5Fget_obj_ids(file_id, obj_type, max_objs, obj_ids) & @@ -952,9 +1009,42 @@ CONTAINS END FUNCTION H5Fget_obj_ids END INTERFACE - c_num_objs = H5Fget_obj_ids(file_id, INT(obj_type, C_INT), max_objs, obj_ids) + ! Don't return the identifiers h5open_f opened, so that this agrees with + ! h5fget_obj_count_f about what the application has open. + num_skip = 0 + IF(file_id.EQ.INT(H5F_OBJ_ALL_F,HID_T)) num_skip = h5open_num_obj_matching(obj_type) hdferr = 0 + + IF(num_skip.EQ.0)THEN + + c_num_objs = H5Fget_obj_ids(file_id, INT(obj_type, C_INT), max_objs, obj_ids) + + ELSE + ! Ask for enough identifiers that max_objs of the application's own can still be + ! returned when h5open_f's are interleaved with them. + ALLOCATE(obj_ids_loc(1:max_objs+num_skip), STAT=alloc_stat) + IF(alloc_stat.NE.0)THEN + hdferr = -1 + RETURN + ENDIF + + c_num_objs = H5Fget_obj_ids(file_id, INT(obj_type, C_INT), max_objs+num_skip, obj_ids_loc) + + IF(c_num_objs.GE.0)THEN + num_kept = 0 + DO idx = 1, c_num_objs + IF(num_kept.EQ.max_objs) EXIT + IF(h5open_owns_id(obj_ids_loc(idx))) CYCLE + num_kept = num_kept + 1 + obj_ids(num_kept) = obj_ids_loc(idx) + ENDDO + c_num_objs = num_kept + ENDIF + + DEALLOCATE(obj_ids_loc) + ENDIF + IF(c_num_objs.LT.0) hdferr = -1 IF (PRESENT(num_objs)) num_objs= c_num_objs diff --git a/fortran/src/H5_ff.F90 b/fortran/src/H5_ff.F90 index 010b3e2f71e..dd1e5276554 100644 --- a/fortran/src/H5_ff.F90 +++ b/fortran/src/H5_ff.F90 @@ -177,10 +177,12 @@ CONTAINS !! \param error \fortran_error !! SUBROUTINE h5open_f(error) - USE H5F, ONLY : h5fget_obj_count_f, H5OPEN_NUM_OBJ + USE H5F, ONLY : h5fget_obj_count_f, H5OPEN_NUM_OBJ, H5OPEN_NUM_OBJ_BY_TYPE IMPLICIT NONE INTEGER, INTENT(OUT) :: error INTEGER(SIZE_T) :: H5OPEN_NUM_OBJ_LOC = 0 + INTEGER, DIMENSION(1:4) :: obj_type_loc + INTEGER :: iobj INTERFACE INTEGER FUNCTION h5init_types_c(p_types, f_types, i_types) & @@ -804,6 +806,20 @@ CONTAINS H5_SZIP_EC_OM_F = H5LIB_flags(1) H5_SZIP_NN_OM_F = H5LIB_flags(2) + ! Record what this call left open so that h5fget_obj_count_f can exclude it. + ! The counts are taken per object type rather than assuming which types the + ! initialization above created. Each count goes through a local variable + ! because h5fget_obj_count_f reads these by use association. + obj_type_loc(1) = H5F_OBJ_FILE_F + obj_type_loc(2) = H5F_OBJ_DATASET_F + obj_type_loc(3) = H5F_OBJ_GROUP_F + obj_type_loc(4) = H5F_OBJ_DATATYPE_F + + DO iobj = 1, 4 + CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), obj_type_loc(iobj), H5OPEN_NUM_OBJ_LOC, error) + H5OPEN_NUM_OBJ_BY_TYPE(iobj) = H5OPEN_NUM_OBJ_LOC + ENDDO + CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), H5F_OBJ_ALL_F, H5OPEN_NUM_OBJ_LOC, error) H5OPEN_NUM_OBJ = H5OPEN_NUM_OBJ_LOC @@ -818,7 +834,7 @@ CONTAINS !! \param error \fortran_error !! SUBROUTINE h5close_f(error) - USE H5F, ONLY : h5fget_obj_count_f, H5OPEN_NUM_OBJ + USE H5F, ONLY : H5OPEN_NUM_OBJ, H5OPEN_NUM_OBJ_BY_TYPE IMPLICIT NONE INTEGER, INTENT(OUT) :: error INTERFACE @@ -843,8 +859,9 @@ CONTAINS floating_types, FLOATING_TYPES_LEN, & integer_types, INTEGER_TYPES_LEN ) - ! Reset the number of open objects from h5open_f to zero - CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), H5F_OBJ_ALL_F, H5OPEN_NUM_OBJ, error) + ! Reset the counts of the objects created by h5open_f. + H5OPEN_NUM_OBJ = 0 + H5OPEN_NUM_OBJ_BY_TYPE = 0 END SUBROUTINE h5close_f diff --git a/fortran/test/fortranlib_test.F90 b/fortran/test/fortranlib_test.F90 index 3b02296bdd2..9e2eb0c1e4b 100644 --- a/fortran/test/fortranlib_test.F90 +++ b/fortran/test/fortranlib_test.F90 @@ -72,6 +72,11 @@ PROGRAM fortranlibtest ! 'Testing FILE Interface ' ! '=========================================' + ret_total_error = 0 + ret_total_error = 0 + CALL objcount_ids(cleanup, ret_total_error) + CALL write_test_status(ret_total_error, ' Object count and identifier test', total_error) + ret_total_error = 0 CALL mountingtest(cleanup, ret_total_error) CALL write_test_status(ret_total_error, ' Mounting test', total_error) diff --git a/fortran/test/tH5A.F90 b/fortran/test/tH5A.F90 index ce69ba11bc0..e85e125799f 100644 --- a/fortran/test/tH5A.F90 +++ b/fortran/test/tH5A.F90 @@ -148,7 +148,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify file name" - STOP + CALL h5_exit_f(1) ENDIF CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f",error,total_error) diff --git a/fortran/test/tH5A_1_8.F90 b/fortran/test/tH5A_1_8.F90 index 0a90842075c..8d4c1428d82 100644 --- a/fortran/test/tH5A_1_8.F90 +++ b/fortran/test/tH5A_1_8.F90 @@ -2524,7 +2524,7 @@ SUBROUTINE test_attr_basic_write(fapl, total_error) WRITE(*,*) 'ERROR: attribute name different: attr_name ='//TRIM(check_name)//'.' WRITE(*,*) ' should be ='//TRIM(ATTR_TMP_NAME)//'.' total_error = total_error + 1 - stop + CALL h5_exit_f(1) ENDIF ! Try with a string buffer that is exactly the correct size diff --git a/fortran/test/tH5D.F90 b/fortran/test/tH5D.F90 index 22c98cc84ee..c3143d068f5 100644 --- a/fortran/test/tH5D.F90 +++ b/fortran/test/tH5D.F90 @@ -84,7 +84,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f", error, total_error) @@ -325,7 +325,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f",error,total_error) @@ -428,7 +428,7 @@ CONTAINS CALL check("h5sget_simple_extent_ndims_f",error,total_error) IF (rankr .NE. RANK) THEN WRITE(*,*) "dataset rank error occurred" - STOP + CALL h5_exit_f(1) END IF ! @@ -438,7 +438,7 @@ CONTAINS CALL check("h5sget_simple_extent_dims_f",error,total_error) IF ((dimsr(1) .NE. dims1(1)) .OR. (dimsr(2) .NE. dims1(2))) THEN WRITE(*,*) "dataset dimensions error occurred" - STOP + CALL h5_exit_f(1) END IF ! @@ -537,7 +537,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5pcreate_f(H5P_FILE_CREATE_F, fcpl, error) @@ -1028,7 +1028,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5pcreate_f(H5P_DATASET_XFER_F, dxpl, error) diff --git a/fortran/test/tH5E.F90 b/fortran/test/tH5E.F90 index c878b3c8efa..b835fd8d4b5 100644 --- a/fortran/test/tH5E.F90 +++ b/fortran/test/tH5E.F90 @@ -61,7 +61,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f",error,total_error) @@ -74,7 +74,7 @@ CONTAINS CALL h5_fixname_f(err_filename, fix_err_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5eprint_f(error, fix_err_filename) CALL h5gopen_f(file_id, "Doesnotexist2", grp_id, tmp_error) diff --git a/fortran/test/tH5F.F90 b/fortran/test/tH5F.F90 index 234f498ffdd..ba1a6a76c6f 100644 --- a/fortran/test/tH5F.F90 +++ b/fortran/test/tH5F.F90 @@ -43,6 +43,198 @@ MODULE TH5F CONTAINS + ! Verify that h5open_f re-initialized the Fortran interface. Enter with the + ! interface closed; returns with it closed again. + SUBROUTINE check_reopen(total_error) + IMPLICIT NONE + INTEGER, INTENT(INOUT) :: total_error + + INTEGER :: error + INTEGER(SIZE_T) :: obj_count ! open object count + INTEGER, DIMENSION(1:5) :: obj_type ! open object type to check + INTEGER(SIZE_T), PARAMETER :: max_ids = 20 + INTEGER(HID_T), DIMENSION(1:max_ids) :: obj_ids + INTEGER(SIZE_T) :: num_objs + LOGICAL :: valid + INTEGER :: i + + CALL h5open_f(error) + CALL check("h5open_f",error,total_error) + + ! A skipped re-initialization leaves the predefined types holding the + ! identifiers h5close_f released, and h5open_f still reports success. + CALL h5iis_valid_f(H5T_NATIVE_INTEGER, valid, error) + CALL check("h5iis_valid_f",error,total_error) + IF(.NOT.valid)THEN + total_error = total_error + 1 + ENDIF + + obj_type(1) = H5F_OBJ_ALL_F + obj_type(2) = H5F_OBJ_FILE_F + obj_type(3) = H5F_OBJ_GROUP_F + obj_type(4) = H5F_OBJ_DATASET_F + obj_type(5) = H5F_OBJ_DATATYPE_F + + ! Only the types belonging to this h5open_f are open, so the ones from the + ! preceding h5open_f were released by h5close_f. + DO i = 1, 5 + CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), obj_type(i), obj_count, error) + CALL check("h5fget_obj_count_f",error,total_error) + IF(obj_count.NE.0)THEN + total_error = total_error + 1 + ENDIF + + ! The identifiers h5open_f opened are not the application's either, so the + ! list must agree with the count. + CALL h5fget_obj_ids_f(INT(H5F_OBJ_ALL_F,HID_T), obj_type(i), max_ids, obj_ids, error, num_objs) + CALL check("h5fget_obj_ids_f",error,total_error) + IF(num_objs.NE.obj_count)THEN + total_error = total_error + 1 + ENDIF + ENDDO + + CALL h5close_f(error) + CALL check("h5close_f",error,total_error) + + END SUBROUTINE check_reopen + + ! Check that the object counts and the identifier lists agree with each other, + ! and that both report only what the application has open. + SUBROUTINE objcount_ids(cleanup, total_error) + IMPLICIT NONE + LOGICAL, INTENT(IN) :: cleanup + INTEGER, INTENT(INOUT) :: total_error + + CHARACTER(LEN=8), PARAMETER :: filename = "objcount" + CHARACTER(LEN=80) :: fix_filename + INTEGER(HID_T) :: fid, gid, did, sid, tid + INTEGER(HID_T), DIMENSION(1:20) :: obj_ids + INTEGER(SIZE_T) :: obj_count, obj_count2, num_objs + INTEGER :: error, i + + CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) + IF (error .NE. 0) THEN + WRITE(*,*) "Cannot modify filename" + CALL h5_exit_f(1) + ENDIF + + CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, fid, error) + CALL check("h5fcreate_f",error,total_error) + CALL h5gcreate_f(fid, "group", gid, error) + CALL check("h5gcreate_f",error,total_error) + CALL h5screate_f(H5S_SCALAR_F, sid, error) + CALL check("h5screate_f",error,total_error) + CALL h5dcreate_f(fid, "dset", H5T_NATIVE_INTEGER, sid, did, error) + CALL check("h5dcreate_f",error,total_error) + + ! One file, one group and one dataset are open. The datatypes h5open_f opened + ! are not the application's and must not appear in either answer. + CALL count_and_list(H5F_OBJ_FILE_F, 1_SIZE_T, total_error) + CALL count_and_list(H5F_OBJ_GROUP_F, 1_SIZE_T, total_error) + CALL count_and_list(H5F_OBJ_DATASET_F, 1_SIZE_T, total_error) + CALL count_and_list(H5F_OBJ_DATATYPE_F, 0_SIZE_T, total_error) + CALL count_and_list(H5F_OBJ_ALL_F, 3_SIZE_T, total_error) + + ! Several types combined with IOR must count as the sum of the parts. + CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), & + IOR(H5F_OBJ_GROUP_F,H5F_OBJ_DATASET_F), obj_count, error) + CALL check("h5fget_obj_count_f",error,total_error) + IF(obj_count.NE.2)THEN + total_error = total_error + 1 + WRITE(*,*) "IOR of two object types did not count as the sum of the parts" + ENDIF + + ! A combination that includes the types h5open_f opened is still adjusted. + CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), & + IOR(H5F_OBJ_GROUP_F,H5F_OBJ_DATATYPE_F), obj_count2, error) + CALL check("h5fget_obj_count_f",error,total_error) + IF(obj_count2.NE.1)THEN + total_error = total_error + 1 + WRITE(*,*) "IOR including datatypes did not exclude the h5open_f objects" + ENDIF + + ! max_objs smaller than the number of open objects must return exactly that + ! many, and all of them the application's. + obj_ids = -1 + CALL h5fget_obj_ids_f(INT(H5F_OBJ_ALL_F,HID_T), H5F_OBJ_ALL_F, 2_SIZE_T, obj_ids, error, num_objs) + CALL check("h5fget_obj_ids_f",error,total_error) + IF(num_objs.NE.2)THEN + total_error = total_error + 1 + WRITE(*,*) "h5fget_obj_ids_f did not fill max_objs identifiers" + ENDIF + DO i = 1, 2 + IF(obj_ids(i).NE.fid .AND. obj_ids(i).NE.gid .AND. obj_ids(i).NE.did)THEN + total_error = total_error + 1 + WRITE(*,*) "h5fget_obj_ids_f returned an identifier the application did not open" + ENDIF + ENDDO + + CALL h5dclose_f(did, error) + CALL check("h5dclose_f",error,total_error) + CALL h5sclose_f(sid, error) + CALL check("h5sclose_f",error,total_error) + CALL h5gclose_f(gid, error) + CALL check("h5gclose_f",error,total_error) + CALL h5fclose_f(fid, error) + CALL check("h5fclose_f",error,total_error) + + ! Nothing of the application's is left, and h5open_f's objects are still hidden. + CALL count_and_list(H5F_OBJ_ALL_F, 0_SIZE_T, total_error) + + ! An application's own datatype is reported even though h5open_f's are not. Here + ! h5open_f's come first in the list, so a short buffer still has to be filled + ! with the application's rather than exhausted on the ones being skipped. + CALL h5tcopy_f(H5T_NATIVE_INTEGER, tid, error) + CALL check("h5tcopy_f",error,total_error) + + CALL count_and_list(H5F_OBJ_DATATYPE_F, 1_SIZE_T, total_error) + + obj_ids = -1 + CALL h5fget_obj_ids_f(INT(H5F_OBJ_ALL_F,HID_T), H5F_OBJ_DATATYPE_F, 1_SIZE_T, obj_ids, error, num_objs) + CALL check("h5fget_obj_ids_f",error,total_error) + IF(num_objs.NE.1 .OR. obj_ids(1).NE.tid)THEN + total_error = total_error + 1 + WRITE(*,*) "h5fget_obj_ids_f did not return the application's datatype" + ENDIF + + CALL h5tclose_f(tid, error) + CALL check("h5tclose_f",error,total_error) + + IF(cleanup) CALL h5_cleanup_f(filename, H5P_DEFAULT_F, error) + CALL check("h5_cleanup_f",error,total_error) + + RETURN + END SUBROUTINE objcount_ids + + ! Check that a count of obj_type is expected and that the identifier list + ! reports the same number. + SUBROUTINE count_and_list(obj_type, expected, total_error) + IMPLICIT NONE + INTEGER, INTENT(IN) :: obj_type + INTEGER(SIZE_T), INTENT(IN) :: expected + INTEGER, INTENT(INOUT) :: total_error + + INTEGER(HID_T), DIMENSION(1:20) :: obj_ids + INTEGER(SIZE_T) :: obj_count, num_objs + INTEGER :: error + + CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), obj_type, obj_count, error) + CALL check("h5fget_obj_count_f",error,total_error) + IF(obj_count.NE.expected)THEN + total_error = total_error + 1 + WRITE(*,*) "h5fget_obj_count_f reported ", obj_count, " instead of ", expected + ENDIF + + CALL h5fget_obj_ids_f(INT(H5F_OBJ_ALL_F,HID_T), obj_type, 20_SIZE_T, obj_ids, error, num_objs) + CALL check("h5fget_obj_ids_f",error,total_error) + IF(num_objs.NE.obj_count)THEN + total_error = total_error + 1 + WRITE(*,*) "h5fget_obj_ids_f reported ", num_objs, " but the count was ", obj_count + ENDIF + + RETURN + END SUBROUTINE count_and_list + SUBROUTINE h5openclose(total_error) IMPLICIT NONE INTEGER, INTENT(INOUT) :: total_error @@ -51,30 +243,19 @@ CONTAINS ! flag to check operation success ! INTEGER :: error - INTEGER(SIZE_T) :: obj_count ! open object count - INTEGER, DIMENSION(1:5) :: obj_type ! open object type to check - INTEGER :: i, j + INTEGER :: j + + ! h5open_f is the only call allowed once h5close_f has closed the Fortran + ! interface, so every check below is made from check_reopen. DO j = 1, 2 CALL h5open_f(error) CALL check("h5open_f",error,total_error) - obj_type(1) = H5F_OBJ_ALL_F - obj_type(2) = H5F_OBJ_FILE_F - obj_type(3) = H5F_OBJ_GROUP_F - obj_type(4) = H5F_OBJ_DATASET_F - obj_type(5) = H5F_OBJ_DATATYPE_F - CALL h5close_f(error) CALL check("h5close_f",error,total_error) - ! Check all the datatypes created during h5open_f are closed in h5close_f - DO i = 1, 5 - CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), obj_type(i), obj_count, error) - CALL check("h5fget_obj_count_f",error,total_error) - IF(obj_count.NE.0)THEN - total_error = total_error + 1 - ENDIF - ENDDO + + CALL check_reopen(total_error) ENDDO ! Test calling h5open_f multiple times without calling h5close_f @@ -85,14 +266,8 @@ CONTAINS CALL h5close_f(error) CALL check("h5close_f",error,total_error) - ! Check all the datatypes created during h5open_f are closed in h5close_f - DO i = 1, 5 - CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), obj_type(i), obj_count, error) - CALL check("h5fget_obj_count_f",error,total_error) - IF(obj_count.NE.0)THEN - total_error = total_error + 1 - ENDIF - ENDDO + + CALL check_reopen(total_error) ! Test calling h5open_f multiple times with a h5close_f in the series of h5open_f DO j = 1, 5 @@ -101,39 +276,21 @@ CONTAINS IF(j.EQ.3)THEN CALL h5close_f(error) CALL check("h5close_f",error,total_error) - ! Check all the datatypes created during h5open_f are closed in h5close_f - DO i = 1, 5 - CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), obj_type(i), obj_count, error) - CALL check("h5fget_obj_count_f",error,total_error) - IF(obj_count.NE.0)THEN - total_error = total_error + 1 - ENDIF - ENDDO + + CALL check_reopen(total_error) ENDIF ENDDO CALL h5close_f(error) CALL check("h5close_f",error,total_error) - ! Check all the datatypes created during h5open_f are closed in h5close_f - DO i = 1, 5 - CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), obj_type(i), obj_count, error) - CALL check("h5fget_obj_count_f",error,total_error) - IF(obj_count.NE.0)THEN - total_error = total_error + 1 - ENDIF - ENDDO + + CALL check_reopen(total_error) ! Check calling h5close_f after already calling h5close_f CALL h5close_f(error) CALL check("h5close_f",error,total_error) - ! Check all the datatypes created during h5open_f are closed in h5close_f - DO i = 1, 5 - CALL h5fget_obj_count_f(INT(H5F_OBJ_ALL_F,HID_T), obj_type(i), obj_count, error) - CALL check("h5fget_obj_count_f",error,total_error) - IF(obj_count.NE.0)THEN - total_error = total_error + 1 - ENDIF - ENDDO + + CALL check_reopen(total_error) RETURN END SUBROUTINE h5openclose @@ -345,14 +502,14 @@ CONTAINS CALL check("h5fis_accessible_f",error,total_error) IF ( .NOT. status ) THEN write(*,*) "File ", fix_filename1, " is not accessible as hdf5" - stop + CALL h5_exit_f(1) END IF CALL h5fis_accessible_f(fix_filename2, status, error) CALL check("h5fis_accessible_f",error,total_error) IF ( .NOT. status ) THEN write(*,*) "File ", fix_filename2, " is not accessible as hdf5" - stop + CALL h5_exit_f(1) END IF ! @@ -362,14 +519,14 @@ CONTAINS CALL check("h5fis_hdf5_f",error,total_error) IF ( .NOT. status ) THEN write(*,*) "File ", fix_filename1, " is not in hdf5 format" - stop + CALL h5_exit_f(1) END IF CALL h5fis_hdf5_f(fix_filename2, status, error) CALL check("h5fis_hdf5_f",error,total_error) IF ( .NOT. status ) THEN write(*,*) "File ", fix_filename2, " is not in hdf5 format" - stop + CALL h5_exit_f(1) END IF ! @@ -575,7 +732,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f",error,total_error) @@ -802,7 +959,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f",error,total_error) @@ -871,7 +1028,7 @@ CONTAINS CALL h5_fixname_f(filename1, fix_filename1, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify file name" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename1, H5F_ACC_TRUNC_F, file1_id, error) CALL check("h5fcreate_f",error,total_error) @@ -907,7 +1064,7 @@ CONTAINS CALL h5_fixname_f(filename2, fix_filename2, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify file name" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename2, H5F_ACC_TRUNC_F, file2_id, error, & prop_id, access_id) @@ -993,7 +1150,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, fid, error) CALL check("h5fcreate_f",error,total_error) @@ -1120,7 +1277,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5pcreate_f(H5P_FILE_CREATE_F, fcpl, error) @@ -1212,7 +1369,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5pcreate_f(H5P_FILE_ACCESS_F, fapl, error) diff --git a/fortran/test/tH5G.F90 b/fortran/test/tH5G.F90 index 30de65766c5..7f66a0ecab0 100644 --- a/fortran/test/tH5G.F90 +++ b/fortran/test/tH5G.F90 @@ -81,7 +81,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f",error,total_error) diff --git a/fortran/test/tH5I.F90 b/fortran/test/tH5I.F90 index baba2df428a..5e9019b77f6 100644 --- a/fortran/test/tH5I.F90 +++ b/fortran/test/tH5I.F90 @@ -107,7 +107,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f",error,total_error) diff --git a/fortran/test/tH5P.F90 b/fortran/test/tH5P.F90 index 477b43060e5..b82fb0ef545 100644 --- a/fortran/test/tH5P.F90 +++ b/fortran/test/tH5P.F90 @@ -71,7 +71,8 @@ SUBROUTINE external_test(cleanup, total_error) ! CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN - STOP "Cannot modify filename" + WRITE(*,*) "Cannot modify filename" + CALL h5_exit_f(1) ENDIF CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f",error,total_error) @@ -263,7 +264,7 @@ SUBROUTINE multi_file_test(cleanup, total_error) CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5pcreate_f(H5P_FILE_ACCESS_F, fapl, error) CALL check("h5pcreate_f", error, total_error) @@ -481,7 +482,7 @@ SUBROUTINE test_chunk_cache(cleanup, total_error) CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF ! Create a default fapl and dapl diff --git a/fortran/test/tH5R.F90 b/fortran/test/tH5R.F90 index ac0319779f7..0493d6acbf6 100644 --- a/fortran/test/tH5R.F90 +++ b/fortran/test/tH5R.F90 @@ -82,7 +82,7 @@ SUBROUTINE refobjtest(cleanup, total_error) CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f",error,total_error) @@ -299,7 +299,7 @@ SUBROUTINE refregtest(cleanup, total_error) CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) ! Default file access and file creation @@ -536,7 +536,7 @@ SUBROUTINE v3reftest(cleanup, total_error) CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f",error,total_error) diff --git a/fortran/test/tH5S.F90 b/fortran/test/tH5S.F90 index 21809203e3f..a6248fd0c23 100644 --- a/fortran/test/tH5S.F90 +++ b/fortran/test/tH5S.F90 @@ -101,7 +101,7 @@ CONTAINS CALL h5_fixname_f(filename1, fix_filename1, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename1, H5F_ACC_TRUNC_F, file1_id, error) CALL check("h5fcreate_f", error, total_error) @@ -109,7 +109,7 @@ CONTAINS CALL h5_fixname_f(filename2, fix_filename2, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename2, H5F_ACC_TRUNC_F, file2_id, error) CALL check("h5fcreate_f", error, total_error) diff --git a/fortran/test/tH5Sselect.F90 b/fortran/test/tH5Sselect.F90 index 0509f33ba3c..bc0e2690ddd 100644 --- a/fortran/test/tH5Sselect.F90 +++ b/fortran/test/tH5Sselect.F90 @@ -158,7 +158,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f", error, total_error) @@ -575,7 +575,7 @@ CONTAINS CALL h5_fixname_f(filename1, fix_filename1, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename1, H5F_ACC_TRUNC_F, file1_id, error) CALL check("h5fcreate_f", error, total_error) @@ -583,7 +583,7 @@ CONTAINS CALL h5_fixname_f(filename2, fix_filename2, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename2, H5F_ACC_TRUNC_F, file2_id, error) CALL check("h5fcreate_f", error, total_error) @@ -978,7 +978,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f", error, total_error) @@ -1104,7 +1104,7 @@ CONTAINS ! ALLOCATE(blocklist(num_blocks*RANK*2), STAT= error) if(error .NE. 0) then - STOP + CALL h5_exit_f(1) endif ! @@ -1146,7 +1146,7 @@ CONTAINS ! ALLOCATE(pointlist(num_blocks*RANK), STAT= error) ALLOCATE(pointlist(20), STAT= error) if(error .NE. 0) then - STOP + CALL h5_exit_f(1) endif ! @@ -1264,7 +1264,7 @@ SUBROUTINE test_select_point(cleanup, total_error) CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF xfer_plist = H5P_DEFAULT_F ! MESSAGE(5, ("Testing Element Selection Functions\n")); diff --git a/fortran/test/tH5T.F90 b/fortran/test/tH5T.F90 index 6424230cdd4..82f0e7848e1 100644 --- a/fortran/test/tH5T.F90 +++ b/fortran/test/tH5T.F90 @@ -186,7 +186,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f", error, total_error) @@ -867,7 +867,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5fcreate_f(fix_filename,H5F_ACC_TRUNC_F,file_id,error) CALL check("h5fcreate_f", error, total_error) @@ -985,7 +985,7 @@ SUBROUTINE test_derived_flt(cleanup, total_error) CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) IF (error .NE. 0) THEN WRITE(*,*) "Cannot modify filename" - STOP + CALL h5_exit_f(1) ENDIF CALL h5fcreate_f(fix_filename,H5F_ACC_TRUNC_F,file,error) diff --git a/fortran/test/tH5VL.F90 b/fortran/test/tH5VL.F90 index 41acf9c81fb..b19315e7cb3 100644 --- a/fortran/test/tH5VL.F90 +++ b/fortran/test/tH5VL.F90 @@ -87,7 +87,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f", error, total_error) @@ -249,7 +249,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f", error, total_error) @@ -407,7 +407,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f", error, total_error) diff --git a/fortran/test/tH5Z.F90 b/fortran/test/tH5Z.F90 index 50acd17dad7..a8324b204b4 100644 --- a/fortran/test/tH5Z.F90 +++ b/fortran/test/tH5Z.F90 @@ -461,7 +461,7 @@ CONTAINS CALL h5_fixname_f(filename, fix_filename, H5P_DEFAULT_F, error) if (error .ne. 0) then write(*,*) "Cannot modify filename" - stop + CALL h5_exit_f(1) endif CALL h5fcreate_f(fix_filename, H5F_ACC_TRUNC_F, file_id, error) CALL check("h5fcreate_f", error, total_error) diff --git a/release_docs/CHANGELOG.md b/release_docs/CHANGELOG.md index a29d859ffe2..3c48ee5bab4 100644 --- a/release_docs/CHANGELOG.md +++ b/release_docs/CHANGELOG.md @@ -353,6 +353,36 @@ We would like to thank the many HDF5 community members who contributed to this r ## Fortran API +### h5open_f now re-initializes the Fortran interface after h5close_f + + An h5open_f / h5close_f / h5open_f sequence could leave the Fortran interface + uninitialized. The second h5open_f reported success, but the predefined type + handles were left holding identifiers that h5close_f had released, so later calls + failed. Whether this happened depended on the Fortran compiler. + + Fixes GitHub issue #6642 + +### h5fget_obj_ids_f no longer returns the Fortran interface's own identifiers + + h5fget_obj_count_f excludes the objects h5open_f opens to represent the predefined + types, but h5fget_obj_ids_f returned them, so the two disagreed about the same query + and an application walking the list found datatypes it never opened. Both now report + only what the application has open, matching the C API. + + Fixes GitHub issue #6648 + +### h5fget_obj_count_f and h5fget_obj_ids_f document their object type argument + + Both listed the object types as alternatives without mentioning that they may be + combined with IOR(), which the C API supports and both have always passed through. + +### h5fget_obj_count_f no longer returns negative counts + + With the Fortran interface open, counting a single object type across all files + subtracted the objects opened by h5open_f, so queries for files, groups, and + datasets returned a negative count and reported success. A negative count is now + reported as an error. + ## High-Level Library ### Fixed critical buffer overflow vulnerability in H5TBget_field_info() (CWE-120) @@ -428,6 +458,25 @@ We would like to thank the many HDF5 community members who contributed to this r ## Testing +### Fortran test programs no longer exit successfully after a fatal error + + The Fortran tests ended unrecoverable failures with STOP, which exits with a + success status, so a run that aborted part way through was reported as passing. + +### New test for the object count and identifier list + + The Fortran tests had no coverage of h5fget_obj_ids_f over all files, and none that + compared it against h5fget_obj_count_f. A new test opens objects of several types + and checks that the two agree, that object types combined with IOR() count as the + sum of their parts, and that a buffer shorter than the number of open objects is + filled with the application's own. + +### The h5open/h5close test checks that the interface re-initializes + + Its object counts were taken while the Fortran interface was closed, where no such + call is permitted. They now run after the interface has been reopened, and confirm + that the predefined types are usable again. + # ✨ Support for new platforms and languages # ☑️ Platforms Tested