Double finalization

Double finalization

Hello,

I don't know how far ifort's support for type-bound final bindings goes right now, but here is some strange behaviour:

module m
    implicit none
 
    type :: derived_type
    ! allocatable component causes rerunning the finalizer
    integer, allocatable :: alloc_comp
 
        ! value 99 indicates that the finalizer already went over this
        integer :: finalized = 0   
 
        ! identification
        character :: ID = '?'
    contains
        final :: finalize
    end type
contains
 
    ! finalizer
    subroutine finalize(arg)
        type(derived_type) :: arg
 
        print *, 'run finalizer on ID = ', arg%ID, ':'
 
        if (arg%finalized == 99) then
            print *, '...finalizing an already finalized object!'
        end if
 
        arg%finalized = 99
    end subroutine
 
    ! main
    subroutine test_finalizer()
        type(derived_type), allocatable :: my_derived_alloc    ! (1)
        type(derived_type) :: my_derived            ! (2)        
 
        ! mark local variable that sould be finalized
        my_derived%ID = 'X'
 
        ! additional code and allocations involving both variables
        ! does not change behaviour (as far as I tested it)
 
    end subroutine
end module
 
program p
    use m
    implicit none
 
    call test_finalizer()
end program

Results:

  • comment out (1): single finalization on local variable     [OK]
  • comment out (2): no finalization (no allocation)             [OK]
  • interchanging lines (1) and (2): single run of finalizer     [STRANGE...]
  • both uncommented: double finalization on loval variable    [FAIL?]

General question:

  • Is there ever a (legal) situation where multiple finalizations of the same object might happen?
  • Are there any situations where finalization nessecary to deallocate derived-type allocatable components?
    (Or is that taken care of in in every possible situation automatically?)

Thank you for advice and comments,

Ferdinand

PS: my ifort version is 14.0.0

10 post / 0 nuovi
Ultimo contenuto
Per informazioni complete sulle ottimizzazioni del compilatore, consultare l'Avviso sull'ottimizzazione
Ritratto di Izaak Beekman

Citazione:

Ferdinand T. ha scritto:

General question:

  • Is there ever a (legal) situation where multiple finalizations of the same object might happen?
  • Are there any situations where finalization nessecary to deallocate derived-type allocatable components?
    (Or is that taken care of in in every possible situation automatically?)

AFAIK, finalization is not necessary for allocatable derived types that only have allocatable components. I believe that whenever an object with the allocatable attribute goes out of scope the standard dictates that it be deallocated. So, for most use cases, you will not need final procedures if your derived types are allocatable.

Can you please include the commands you used to compile the code, and the output?

-Zaak

Hello Zaak,

thank you for the reply! I just recompiled my sample code with ifort version  14.0.1:

Commands (no flags used):

ifort finalization_twice.f90
./a.out

(the source file's name is finalization_twice.f90)

Output:

  • comment out (1): single finalization on local variable     [OK] 
    run finalizer on ID = X:

  • comment out (2): no finalization (no allocation)             [OK]
    (note: also commented out the line my_derived%ID = 'X')
    - no outpout -
  • interchanging lines (1) and (2): single run of finalizer     [STRANGE...]
     run finalizer on ID = X:
  • both uncommented: double finalization on loval variable    [FAIL?]
     run finalizer on ID = X:
     run finalizer on ID = X:
     ...finalizing an already finalized object!

In the last case (e.g. the unmodified sample program as stated above), the output indicates that the finalizer is invoked twice.

Concerning the general question on finalization of allocatable components: Here is a sample program that shows a memory leak if no finalization is used (probably a compiler issue, then?)

module m
    implicit none
 
    ! extendable type
    type :: component_type
       integer(4) :: some_field        ! cause 4-bit leak 
    end type
 
    ! container with polymorphic component
    type :: derived_type
        class(component_type), allocatable :: alloc_comp    ! use 'type' and leak vanishes
    contains
        !final :: finalize_derived    ! comment in and leak vanishes
    end type
 
contains
    ! finalizer to deallocate components
    subroutine finalize_derived(arg)
        type(derived_type) :: arg
        if (allocated(arg%alloc_comp)) deallocate(arg%alloc_comp)
    end subroutine
 
    ! contructor to allocate components
    function create_derived()
        type(derived_type) :: create_derived
        allocate(create_derived%alloc_comp)
    end function
end module
 
program p
    use m
    implicit none
    type(derived_type) :: my_derived
 
    ! derived type assignment causes memory leak (an alloc_comp gets lost?)
    my_derived = create_derived()
end program

compilation commands:

ifort finalization_twice.f90

valgrind invocation:

valgrind --tool=memcheck ./a.out

valgrind output:

==3888== LEAK SUMMARY:
==3888==    definitely lost: 4 bytes in 1 blocks
==3888==    indirectly lost: 0 bytes in 0 blocks
==3888==      possibly lost: 0 bytes in 0 blocks
==3888==    still reachable: 36 bytes in 2 blocks
==3888==         suppressed: 0 bytes in 0 blocks

That behaviour causes heavy memory leaks in my original code, which I can only avoid using finalizers.

Best regards, Ferdinand

Ritratto di Steve Lionel (Intel)

The double finalization has been escalated as issue DPD200249474. I am somewhat surprised by this as we did a lot of work on finalization lists in 14.0. Obviously not enough.

I will also look at your memory leak example.

Steve
Ritratto di Steve Lionel (Intel)

The memory leak is escalated as issue DPD200249476. Note that the language specifies that no finalization or automatic deallocation of my_derived is done at the end of the program, so there will always be one "leak", but the alloc_comp of the function result should have been deallocated after the assignment.

Steve

Steve, thank you for investigating these issues. The leak-example might be over-simplified, but moving the leaky code into a subroutine doesn't change the behaviour. My experience is that valgrind identifies allocations from the main program as 'still reachable' (since no automatic deallocation is done), but never as a loss.

Regards, Ferdinand

Ritratto di Steve Lionel (Intel)

Right - I simply mentioned the main program thing because valgrind should show one leak. There shouldn't be two.

Steve
Ritratto di Steve Lionel (Intel)

This has been fixed in our sources. I expect the fix to appear in a release later this year.

Steve
Ritratto di jimdempseyatthecove

Steve, back on #5 "Note that the language specifies that no finalization or automatic deallocation of my_derived is done at the end of the program"

In the sample code listing my_derived was not declared itself as allocatable (though a member variable within was).

What is the reason for no finalization?

a) lack of allocatable on my_derived?
b) the fact that it exists in the scope of PROGRAM (iow, had it appeared in a subroutine, the member variable would have been deleted)?

Jim Dempsey

www.quickthreadprogramming.com
Ritratto di Steve Lionel (Intel)

b

"If image execution is terminated, either by an error (e.g. an allocation failure) or by execution of a stop-stmt , error-stop-stmt , or end-program-stmt , entities existing immediately prior to termination are not finalized."

Steve

Accedere per lasciare un commento.