Skip to content

Commit 9698bd8

Browse files
committed
add destructor, correct handle leaks
1 parent 4c1107c commit 9698bd8

File tree

9 files changed

+316
-152
lines changed

9 files changed

+316
-152
lines changed

src/interface.f90

Lines changed: 106 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@ module h5mpi
66
use mpi, only : MPI_COMM_WORLD, MPI_INFO_NULL, mpi_comm_rank
77
use hdf5, only : &
88
HID_T, HSIZE_T, SIZE_T, &
9+
H5I_FILE_F, &
910
H5T_INTEGER_F, H5T_FLOAT_F, H5T_STRING_F, &
1011
H5T_NATIVE_CHARACTER, H5T_NATIVE_INTEGER, H5T_NATIVE_REAL, H5T_NATIVE_DOUBLE, H5T_STD_I64LE, &
1112
H5F_ACC_RDONLY_F, H5F_ACC_TRUNC_F, H5F_ACC_RDWR_F, H5F_SCOPE_GLOBAL_F, &
13+
H5F_OBJ_FILE_F, H5F_OBJ_GROUP_F, H5F_OBJ_DATASET_F, H5F_OBJ_DATATYPE_F, H5F_OBJ_ALL_F, &
1214
H5FD_MPIO_COLLECTIVE_F, &
1315
H5P_DEFAULT_F, H5P_FILE_ACCESS_F, H5P_DATASET_CREATE_F, H5P_DATASET_XFER_F, &
1416
H5S_ALL_F, H5S_SELECT_SET_F, &
1517
H5D_CHUNKED_F, H5D_CONTIGUOUS_F, H5D_COMPACT_F, &
1618
h5dcreate_f, h5dopen_f, h5dclose_f, h5dget_space_f, h5dget_create_plist_f, &
1719
h5fopen_f, h5fclose_f, h5fcreate_f, h5fget_filesize_f, h5fflush_f, h5fis_hdf5_f, &
20+
h5fget_obj_count_f, h5fget_obj_ids_f, h5fget_name_f, &
21+
h5iis_valid_f, h5iget_type_f, h5iget_name_f, &
1822
h5pcreate_f, h5pclose_f, h5pset_chunk_f, h5pset_dxpl_mpio_f, h5pset_fapl_mpio_f, h5pall_filters_avail_f, &
19-
h5sselect_hyperslab_f, h5screate_simple_f, h5sclose_f, &
23+
h5sselect_hyperslab_f, h5sselect_none_f, h5screate_simple_f, h5sclose_f, &
2024
h5get_libversion_f, &
2125
h5open_f, h5close_f
2226

@@ -30,8 +34,10 @@ module h5mpi
3034

3135
character(:), allocatable :: filename
3236
integer(HID_T) :: file_id
33-
logical :: is_open = .false.
37+
3438
logical :: use_mpi = .false.
39+
integer :: mpi_id = -1
40+
3541
logical :: debug = .false.
3642
logical :: parallel_compression = .false.
3743
logical :: fletcher32 = .false.
@@ -49,7 +55,8 @@ module h5mpi
4955
class => get_class, dtype => get_native_dtype, &
5056
deflate => get_deflate, &
5157
layout => hdf_get_layout, chunks => hdf_get_chunk, &
52-
is_contig => hdf_is_contig, is_chunked => hdf_is_chunked, is_compact => hdf_is_compact
58+
is_contig => hdf_is_contig, is_chunked => hdf_is_chunked, is_compact => hdf_is_compact, &
59+
is_open
5360
!! procedures without mapping
5461

5562
generic, public :: write => h5write_scalar,ph5write_1d, ph5write_2d, ph5write_3d, ph5write_4d, ph5write_5d, ph5write_6d, ph5write_7d
@@ -62,6 +69,9 @@ module h5mpi
6269
procedure, private :: h5read_scalar, ph5read_1d, ph5read_2d, ph5read_3d, ph5read_4d, ph5read_5d, ph5read_6d, ph5read_7d
6370
!! mapped procedures must be declared again like this
6471

72+
!> flush file to disk and close file if user forgets to do so.
73+
final :: destructor
74+
6575
end type hdf5_file
6676

6777

@@ -73,7 +83,7 @@ module h5mpi
7383

7484
private
7585
public :: mpi_h5comm, hdf5_file, mpi_tags, has_parallel_compression, is_hdf5, &
76-
check, hdf_wrapup, hdf_rank_check, hdf_shape_check, mpi_collective, mpi_hyperslab, &
86+
check, hdf_rank_check, hdf_shape_check, mpi_collective, mpi_hyperslab, &
7787
hdf5version, HSIZE_T
7888

7989
interface !< write.f90
@@ -84,7 +94,7 @@ module subroutine hdf_create(self, dname, dtype, mem_dims, dset_dims, filespace,
8494
character(*), intent(in) :: dname
8595
integer(HID_T), intent(in) :: dtype
8696
integer(HSIZE_T), dimension(:), intent(in) :: mem_dims, dset_dims
87-
integer(HID_T), intent(out), optional :: filespace, memspace, dset_id
97+
integer(HID_T), intent(out) :: filespace, memspace, dset_id
8898
integer(HSIZE_T), dimension(:), intent(in), optional :: istart, iend
8999
integer, dimension(:), intent(in), optional :: chunk_size
90100
logical, intent(in), optional :: compact
@@ -324,6 +334,11 @@ subroutine ph5open(self, filename, action, mpi, comp_lvl, shuffle, fletcher32, d
324334

325335
if (present(mpi)) self%use_mpi = mpi
326336

337+
if(self%use_mpi) then
338+
call mpi_comm_rank(MPI_COMM_WORLD, self%mpi_id, ierr)
339+
if(ierr /= 0) error stop "ERROR:h5fortran:open: could not get MPI ID"
340+
endif
341+
327342
if(present(debug)) self%debug = debug
328343

329344
call get_hdf5_config(self%parallel_compression)
@@ -394,7 +409,6 @@ subroutine ph5open(self, filename, action, mpi, comp_lvl, shuffle, fletcher32, d
394409
endif
395410

396411
self%filename = filename
397-
self%is_open = .true.
398412

399413
end subroutine ph5open
400414

@@ -411,41 +425,106 @@ subroutine ph5close(self, close_hdf5_interface)
411425

412426
class(hdf5_file), intent(inout) :: self
413427
logical, intent(in), optional :: close_hdf5_interface
414-
integer :: ier, ierr, mpi_id
415428

416-
if (.not. self%is_open) then
429+
integer :: ierr, i
430+
integer(SIZE_T) :: Ngroup, Ndset, Ndtype, Nfile, Lf_name, Lds_name
431+
integer(HID_T), allocatable :: obj_ids(:)
432+
integer(SIZE_T), parameter :: L = 2048 !< arbitrary length
433+
character(L) :: file_name, dset_name
434+
435+
if (.not. self%is_open()) then
417436
write(stderr,*) 'WARNING:h5fortran:file_close: file handle is already closed: '// self%filename
418437
return
419438
endif
420439

440+
!> ref count for better error messages, as this is more of a problem with HDF5-MPI programs
441+
call h5fget_obj_count_f(self%file_id, H5F_OBJ_GROUP_F, Ngroup, ierr)
442+
if(ierr /= 0) error stop "ERROR:h5fortran:close:h5fget_obj_count: could not count open groups: " // self%filename
443+
if(Ngroup > 0) write(stderr,'(a,i0,a)') "ERROR:h5fortran:close: there are ", Ngroup, " groups open: " // self%filename
444+
445+
446+
call h5fget_obj_count_f(self%file_id, H5F_OBJ_DATASET_F, Ndset, ierr)
447+
if(ierr /= 0) error stop "ERROR:h5fortran:close:h5fget_obj_count: could not count open datasets: " // self%filename
448+
if(Ndset > 0) then
449+
write(stderr,'(a,i0,a)') "ERROR:h5fortran:close: there are ", Ndset, " datasets open: " // self%filename
450+
451+
allocate(obj_ids(Ndset))
452+
call h5fget_obj_ids_f(self%file_id, H5F_OBJ_DATASET_F, Ndset, obj_ids, ierr)
453+
if(ierr /= 0) error stop "ERROR:h5fortran:close:h5fget_obj_ids: could not get open dataset ids: " // self%filename
454+
455+
do i = 1, int(Ndset)
456+
call h5fget_name_f(obj_ids(i), file_name, Lf_name, ierr)
457+
if(ierr /= 0) error stop "ERROR:h5fortran:close:h5fget_name: could not get filename of open dataset: " // self%filename
458+
459+
call h5iget_name_f(obj_ids(i), dset_name, L, Lds_name, ierr)
460+
461+
write(stderr,*) "h5fortran:close: open dataset: " // dset_name(:Lds_name) // " in file: " // file_name(:Lf_name)
462+
end do
463+
endif
464+
465+
call h5fget_obj_count_f(self%file_id, H5F_OBJ_DATATYPE_F, Ndtype, ierr)
466+
if(ierr /= 0) error stop "ERROR:h5fortran:close:h5fget_obj_count: could not count open datatypes: " // self%filename
467+
if(Ndtype > 0) write(stderr,'(a,i0,a)') "ERROR:h5fortran:close: there are ", Ndtype, " datatypes open: " // self%filename
468+
469+
call h5fget_obj_count_f(self%file_id, H5F_OBJ_FILE_F, Nfile, ierr)
470+
if(ierr /= 0) error stop "ERROR:h5fortran:close:h5fget_obj_count: could not count open files: " // self%filename
471+
if(Nfile < 1) write(stderr,'(a,i0,a)') "ERROR:h5fortran:close: there are ", Nfile, " files open: " // self%filename
472+
473+
if(Ngroup > 0 .or. Ndset > 0 .or. Ndtype > 0) error stop "ERROR:h5fortran:close: hanging HID handles open: " // self%filename
474+
421475

422476
!> close hdf5 file
423-
call h5fclose_f(self%file_id, ier)
424-
if (ier/=0) then
425-
call mpi_comm_rank(MPI_COMM_WORLD, mpi_id, ierr)
426-
write(stderr,'(a,i0)') 'ERROR:h5fortran:h5fclose: HDF5 file close: ' // self%filename // ' mpi_id: ', mpi_id
477+
call h5fclose_f(self%file_id, ierr)
478+
if (ierr /= 0) then
479+
write(stderr,'(a,i0)') 'ERROR:h5fortran:h5fclose: HDF5 file close: ' // self%filename // ' mpi_id: ', self%mpi_id
427480
error stop
428481
endif
429482

430483
if (present(close_hdf5_interface)) then
431484
if (close_hdf5_interface) then
432-
call h5close_f(ier)
433-
if (ier/=0) error stop 'ERROR: HDF5 library close'
485+
call h5close_f(ierr)
486+
if (ierr /= 0) error stop 'ERROR:h5fortran:h5close: HDF5 library close'
434487
endif
435488
endif
436489

437-
!> sentinel file_id
438-
self%file_id = 0
490+
end subroutine ph5close
439491

440-
self%is_open = .false.
441492

442-
end subroutine ph5close
493+
logical function is_open(self)
494+
!! check if file handle is open
495+
496+
class(hdf5_file), intent(in) :: self
497+
498+
! integer :: hid_type
499+
integer :: ierr
500+
501+
call h5iis_valid_f(self%file_id, is_open, ierr)
502+
if(ierr /= 0) error stop "h5fortran:is_open:h5iis_valid: " // self%filename
503+
504+
! call h5iget_type_f(self%file_id, hid_type, ierr)
505+
! if(ierr /= 0 .or. hid_type /= H5I_FILE_F) is_open = .false.
506+
507+
end function is_open
508+
443509

444510
logical function has_parallel_compression()
445511
call get_hdf5_config(has_parallel_compression)
446512
end function has_parallel_compression
447513

448514

515+
subroutine destructor(self)
516+
!! Close file and handle if user forgets to do so
517+
518+
type(hdf5_file), intent(inout) :: self
519+
520+
if (.not. self%is_open()) return
521+
522+
print '(a)', "auto-closing " // self%filename
523+
call self%close()
524+
525+
end subroutine destructor
526+
527+
449528
logical function hdf_is_contig(self, dname)
450529
class(hdf5_file), intent(in) :: self
451530
character(*), intent(in) :: dname
@@ -638,38 +717,15 @@ logical function hdf_exist(self, dname) result(exists)
638717

639718
integer :: ierr
640719

641-
if(.not.self%is_open) error stop 'h5fortran:exist: file handle is not open'
720+
if(.not. self%is_open()) error stop 'h5fortran:exist: file handle is not open: ' // self%filename
642721

643722
call h5ltpath_valid_f(self%file_id, dname, .true., exists, ierr)
644723
!! h5lexists_f can false error with groups--just use h5ltpath_valid
645-
646724
if (ierr/=0) error stop 'h5fortran:check_exist: could not determine status of ' // dname // ' in ' // self%filename
647725

648-
649726
end function hdf_exist
650727

651728

652-
subroutine hdf_wrapup(filespace, memspace, dset_id, plist_id)
653-
654-
integer(HID_T), intent(in) :: filespace, memspace, dset_id, plist_id
655-
656-
integer :: ierr
657-
658-
call h5dclose_f(dset_id, ierr)
659-
if(ierr/=0) error stop "ERROR: closing dataset"
660-
661-
if(memspace /= H5S_ALL_F) call h5sclose_f(memspace, ierr)
662-
if(ierr/=0) error stop "ERROR: closing memory dataspace"
663-
664-
if(filespace /= H5S_ALL_F) call h5sclose_f(filespace, ierr)
665-
if(ierr/=0) error stop "ERROR: closing file dataspace"
666-
667-
call h5pclose_f(plist_id, ierr)
668-
if(ierr/=0) error stop "ERROR: closing property"
669-
670-
end subroutine hdf_wrapup
671-
672-
673729
subroutine hdf_rank_check(self, dname, mrank, vector_scalar)
674730

675731
class(hdf5_file), intent(in) :: self
@@ -683,9 +739,9 @@ subroutine hdf_rank_check(self, dname, mrank, vector_scalar)
683739

684740
if(present(vector_scalar)) vector_scalar = .false.
685741

686-
if(.not.self%is_open) error stop 'h5fortran:rank_check: file handle is not open'
742+
if(.not.self%is_open()) error stop 'ERROR:h5fortran:rank_check: file handle is not open: ' // self%filename
687743

688-
if (.not.self%exist(dname)) error stop 'ERROR: ' // dname // ' does not exist in ' // self%filename
744+
if (.not.self%exist(dname)) error stop 'ERROR::h5fortran:rank_check: ' // dname // ' does not exist in ' // self%filename
689745

690746
!> check for matching rank, else bad reads can occur--doesn't always crash without this check
691747
call h5ltget_dataset_ndims_f(self%file_id, dname, drank, ierr)
@@ -726,14 +782,14 @@ subroutine hdf_shape_check(self, dname, dims, dset_dims)
726782

727783
call h5ltget_dataset_info_f(self%file_id, dname, dims=dsdims, &
728784
type_class=type_class, type_size=type_size, errcode=ierr)
729-
if (ierr/=0) error stop 'h5fortran:shape_check: get_dataset_info ' // dname // ' read ' // self%filename
785+
if (ierr/=0) error stop 'ERROR:h5fortran:shape_check: get_dataset_info ' // dname // ' read ' // self%filename
730786

731787
if(present(dset_dims)) dset_dims = dsdims
732788

733789
if(self%use_mpi) return
734790

735791
if(any(int(dims, int64) /= dsdims)) then
736-
write(stderr,*) 'h5fortran:shape_check: shape mismatch ' // dname // ' = ', dsdims, ' variable shape =', dims
792+
write(stderr,*) 'ERROR:h5fortran:shape_check: shape mismatch ' // dname // ' = ', dsdims, ' variable shape =', dims
737793
error stop
738794
endif
739795

@@ -748,14 +804,12 @@ integer(HSIZE_T) function hdf_filesize(self)
748804

749805
logical :: close_self
750806

751-
close_self = .false.
807+
close_self = .not. self%is_open()
808+
809+
if (close_self) call self%open(self%filename, action="r", mpi=.false.)
752810

753-
if (.not. self%is_open) then
754-
close_self = .true.
755-
call self%open(self%filename, action="r", mpi=.false.)
756-
endif
757811
call h5fget_filesize_f(self%file_id, hdf_filesize, ierr)
758-
if(ierr/=0) error stop "could not get file size " // self%filename
812+
if(ierr/=0) error stop "ERROR:h5fortran: could not get file size " // self%filename
759813

760814
if(close_self) call self%close()
761815

0 commit comments

Comments
 (0)