Correct execution of Final routine in Fortran
Asked Answered
E

1

1

I have following Fortran code

  type t_octree
     integer                        :: max_num_point
     class(t_octree_node), pointer  :: root => null()
     contains
     final                         :: DESTROY_OCTREE
  end type t_octree

  type t_octree_node
     real                          :: box(2,3)
     integer                       :: depth
     type(t_octree_node), pointer  :: parent      => null()
     type(t_octree_node), pointer  :: children(:) => null()
     class(t_octree_leaf), pointer :: leaf        => null()
  contains
     final                         :: CLEAN_NODE
  end type t_octree_node
  type, extends(t_octree_node) :: t_octree_leaf
     integer, allocatable          :: id(:)
     integer                       :: num_point
  contains
     final                          :: DESTROY_LEAF
  end type

After I have done my processing and now need to make sure that my stuff is deallocated properly.

In my `final routines I have

  subroutine DESTROY_OCTREE( this )

  implicit none

  type(t_octree)               :: this
  type(t_octree_node), pointer :: node => null()
  integer                      :: i


  node => this% root
  if( associated(node% leaf) )deallocate( node% leaf )

  if(.not. associated(node% children) )RETURN
  deallocate(  node )
  node => this% root
  i = 0
  nullify(node)
  print*, associated(this% root) ! this is true!! 
  print*, associated(this% root% children) ! this is true!! 



  end subroutine DESTROY_OCTREE

  recursive subroutine CLEAN_NODE ( this )

  implicit none

  type(t_octree_node)           :: this
  type(t_octree_node), pointer  :: node => null(), next => null()
  integer                       :: i

  if( associated(this% leaf) )then
     deallocate( this% leaf )
     nullify( this% leaf )
  endif
  if(.not. associated(this% children) )RETURN
  do i = 1, 8
     node => this% children(i)
     deallocate( node )
     nullify(node)
    ! debug
    ! print*, i, "rec"
  enddo
   nullify(this% children)
   nullify(this% parent)

  end subroutine CLEAN_NODE

  subroutine DESTROY_LEAF ( leaf )

  implicit none

  type(t_octree_leaf)        :: leaf

  deallocate( leaf% id )

  end subroutine DESTROY_LEAF

In my main program I do following

  program TEST_OCTREE
  use MD_OCTREE

  implicit none

  type(t_octree), pointer               :: octree

  octree      => t_octree( max_num_point,  box )
  (...) ! all processing to build the data structure

Now I deallocate by simply

  deallocate( octree )  
  print*, associated(octree% root) ! this give a segmentation fault

The question Can somebody explain why it seems that my print*, associated(this% root) commands still show TRUE while when printing in my main program it looks like it has been deallocated as it gives me a segmentation fault

Eclair answered 27/12, 2018 at 21:48 Comment(0)
B
3

Deallocating a pointer causes the pointer association status of any other pointer that was associated with the same target to become undefined (F2018 9.7.3.3p2). This situation happens with node and this%root in the DESTROY_OCTREE procedure - both pointers reference the same object (via the pointer assignment node => this% root, the object is deallocated through the node pointer - which makes this%root have undefined association status.

The argument to the ASSOCIATED intrinsic must not be a pointer with undefined association status (F2018 16.9.16p3). The code is non-conforming, anything may happen - where "anything" quite reasonably includes the results you see.

When you deallocate the object through the node pointer, there is no simple way that the processor can also reliably update the status of the this%root pointer - it ends up pointing at something that no longer exists.

There are other suspicious constructs in the fragments of source shown, including use of a superfluous NULLIFY after a DEALLOCATE statement on the same pointer object (successful deallocation disassociates (nullifies) the pointer), use of nullify when perhaps DEALLOCATE is appropriate (hard to say without complete code), and use of what appears to be a structure constructor as a pointer target.

Baudin answered 27/12, 2018 at 22:48 Comment(12)
Thanks for the answer. In other words my destructor has essentially deallocated my tree structure. I see what you mean regarding NULLIFY after DEALLOCATE statements. What else is suspicious?Eclair
Also, what do you mean by the code is non-conforming?Eclair
The OP created constructors as pointer functions with allocations. I tried to atress it is a bad practice, but the usage of the constructor as a pointer target is probably connected to that.Heffernan
@VladimirF, Are you referring to my octree => t_octree( max_num_point, box )where octree is a pointer. ? If yes then this is what my problem is: I did before just do octree=t_octree( max_num_point, box ), but then as soon as the construction was finished, the destruction, final, was executed. When having a pointer I can control when the final is invoked by just having the explicit DEALLOCATE statementEclair
So when I did octree=t_octree( max_num_point, box ), the final routine was also executed, while it did not execute before the program exitedEclair
correction: seems like both cases, whether octree has or hasn't the pointe attribute is calls the FINALIZATION, but as i understand it the left assignment has not been deallocated. However, at termination it seems that the FINALIZATION is not executed for octree. So my linked list pointers are not deallocated at the end of my program ?Eclair
Objects that exist at program termination are not finalized. Avoid functions that have a pointer result, unless you really know what you are doing - use a subroutine instead.Baudin
So in that way you really want to deallocate the objects explicitly ? What is the danger of using functions that have a pointer result ?Eclair
I don't know what you mean by your first sentence. As an example of why pointer functions can be problematic - functions that are intended for use only on the right hand side of a pointer assignment statement will also be accepted on the right hand side of a normal assignment statement. It is only one source character different - it looks "normal" when you read the source, it is not something a compiler can diagnose, runtime problems may only manifest in subtle ways.Baudin
So basically, ptr=myfunc(x) and ptr=>myfunc(x) are both accepted by compiler but only the latter makes sense when myfunc returns a pointer. what i meant is if the objects are not finalized at termination, as I initially thought, I would need to deallocate those explicitly, or whenever I don't want them any longer in my program to free the memoryEclair
There are cases where it makes sense for functions with a pointer result to be used on the right hand side of a normal assignment statement - it depends on what the function does. Addressing this in any detail is not appropriate in comments - perhaps you could ask a new question. If your program is terminating, then it is about to to cease to exist - worrying about objects and memory used by something that is about to completely disappear is moot.Baudin
I added a new question on this as I am very interested to learn what the differences are and when to use what/why #53976800Eclair

© 2022 - 2024 — McMap. All rights reserved.