Passing on procedure pointers in gfortran leads to segfault - pointers

I have a procedure pointer that I need to pass on a few functions down and it crashes when compiling with gfortran (but not with ifort). Here is a minimal example to demonstrate the problem:
module mod1
abstract interface
function f(x)
double precision f
double precision, intent(in) :: x
end function f
end interface
contains
subroutine printme(g)
procedure(f), pointer, intent(in) :: g
write(*,*) g(1d0), g(2d0), g(3d0)
end subroutine printme
subroutine printme2(g)
procedure(f), pointer, intent(in) :: g
call printme(g)
end subroutine printme2
end module mod1
program test
use mod1
procedure(f), pointer :: pg
pg => g
call printme2(pg)
contains
function g(x)
double precision g
double precision, intent(in) :: x
g = x**2
return
end function g
end program test
Obviously in my program, my version of "printme2" does a little more than just that, but you get the point. It calls another routine lots of times and passes on the procedure pointer every single time. Now with the Intel compiler this works as expected:
$ ifort segfault.f90 && ./a.out
1.00000000000000 4.00000000000000 9.00000000000000
However, with gfortran (v4.4.5-8):
$ gfortran segfault.f90 && ./a.out
Segmentation fault
Note that it works if I replace printme2 with printme in the test program.
Why does this happen? What am I doing wrong and how do I make it right?

Related

Deallocating arrays defined from c_f_pointer

The following code compiles in both GNU gfortran and Intel ifort. But only the gfortran compiled version will run successfully.
program fort_tst
use iso_c_binding
INTEGER, POINTER :: a(:)
TYPE(C_PTR) :: ptr
INTEGER, POINTER :: b(:)
ALLOCATE(a(5))
ptr = c_loc(a)
CALL c_f_pointer(ptr,b,[5])
DEALLOCATE(b)
end program fort_tst
The error in the Intel compiled code is :
forrtl: severe (173): A pointer passed to DEALLOCATE points to an object that cannot be deallocated
Image PC Routine Line Source
fort_tst 000000000040C5A1 Unknown Unknown Unknown
fort_tst 0000000000403A17 Unknown Unknown Unknown
fort_tst 0000000000403812 Unknown Unknown Unknown
libc-2.17.so 00002AAAAB20F555 __libc_start_main Unknown Unknown
fort_tst 0000000000403729 Unknown Unknown Unknown
The gfortran code runs to completion. A quick valgrind check does not find any leaks.
Can someone confirm whether the code above is valid/legal code?
I am running
ifort (IFORT) 2021.2.0 20210228
and
GNU Fortran (GCC) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
UPDATE :
What is interesting is that gfortran does the right thing, (i.e. deallocates only allocated memory), even when the user tries to confound it with improper index remapping, or a bogus shape argument. So the internal array descriptor is being properly copied over with gfortran's c_f_pointer.
The error is issued, because the compiler claims that the pointer that is being allocated was not allocated by an allocate statement.
The rules are (F2018):
9.7.3.3 Deallocation of pointer targets
1 If a pointer appears in a DEALLOCATE statement, its association status shall be defined.
Deallocating a pointer that is disassociated or whose target was not
created by an ALLOCATE statement causes an error condition in the
DEALLOCATE statement. If a pointer is associated with an allocatable
entity, the pointer shall not be deallocated. A pointer shall not be
deallocated if its target or any subobject thereof is argument
associated with a dummy argument or construct associated with an
associate name.
Your pointer b was associated using the c_f_pointer subroutine. The error condition mentioned is the
forrtl: severe (173): A pointer passed to DEALLOCATE points to an object that cannot be deallocated
Now we have to be careful, the exact wording is
or whose target was not created by an ALLOCATE statement
The target arguably was created by an allocatable statement. And then went through this indirect chain of association. I am not such an expert language lawyer to be sure whether this makes the target to be applicable or not, when it passed through c_loc() and c_f_pointer().
Gfortran does not issue this error condition and then it works fine because at the end of the day, under the hood, what matters is that the address passed to the system free() function was allocated by the matching system malloc() function.
I think we can conclude that one of the compilers is wrong here, because the mention of the error condition is clear in the standard and either it should be issued or it should not. A third option, that gfortran just leaves it too work, should not happen. Either it is allowed, or an error condition shall be issued.
Re UPDATE: What gfortran does is really sending the address to free(). As long as the pointer is contiguous and starts at the first element, it will work in practice. The size is not necessary and is not passed to free(). The system allocator malloc()/free() stores the size of each allocated system in its own database.
There are even worse abuse cases that can happen and will work just by chance due to this, even if completely illegal in Fortran.
See this:
use iso_c_binding
character, allocatable, target :: a
type(c_ptr) :: p
real, pointer :: b(:)
allocate(a)
p = c_loc(a)
call c_f_pointer(p, b, [1000])
deallocate(b)
end
gfortran is arguably missing a diagnostics opportunity when it comes to the DEALLOCATE statement. ifort is arguably too conservative when it comes to the DEALLOCATE statement.
The error message from ifort is an explicit design choice prohibiting the pointer from C_F_POINTER appearing in a DEALLOCATE statement:
Since the resulting data pointer fptr could point to a target that was not allocated with an ALLOCATE statement, fptr cannot be freed with a DEALLOCATE statement.
There seems little in Fortran 2018 explicitly to support that restriction (even in the case where the target was created by an ALLOCATE statement), and ifort itself isn't consistent in applying it:
use iso_c_binding
integer, pointer :: a, b
type(c_ptr) :: ptr
allocate(a)
ptr = c_loc(a)
call c_f_pointer(ptr,b)
deallocate(b)
end program
However, consider the case
use iso_c_binding
integer, pointer, dimension(:) :: a, b
type(c_ptr) :: ptr
allocate(a(5))
ptr = c_loc(a)
call c_f_pointer(ptr,b,[4])
deallocate(b)
end program
One would surely expect deallocation here to be problematic but this doesn't cause an error condition with gfortran: gfortran isn't carefully checking whether the target is deallocatable (note that it doesn't have to).
There is some subtlety in Fortran 2018's wording of C_F_POINTER (F2018 18.2.3.3)
If both X and FPTR are arrays, SHAPE shall specify a size that is less than or equal to the size of X, and FPTR becomes associated with the first PRODUCT (SHAPE) elements of X (this could be the entirety of X).
and whether "the entirety" of a forms a valid thing to deallocate but ifort's documentation is seemingly too strict and gfortran's checking is not going to catch all invalid cases. There is a case for talking to the vendor of each compiler.
That said, the use of a C_F_POINTER's pointer in a DEALLOCATE statement clearly is more prone to error than "simpler" pointers, and these errors are not ones where we can rely on a compiler to point them out. Even with a conclusion of "clearly this is allowed" I personally would recommend that one avoids this approach where possible without other bad things.
Usage of c_f_pointer is pretty standard behavior in case a Fortran derived type is to be passed to a C++ class as an opaque pointer type, see e.g. the following interoperable class:
module mytype_m
use iso_c_binding
implicit none
private
type, public :: mytype
real, allocatable :: data(:)
contains
procedure :: destroy
procedure :: init
procedure :: printout
end type mytype
public :: mytype_print_c
public :: mytype_init_c
public :: mytype_destroy_c
contains
subroutine init(this,data)
class(mytype), intent(inout), target :: this
real, intent(in) :: data(:)
call destroy(this)
this%data = data
end subroutine init
elemental subroutine destroy(this)
class(mytype), intent(inout), target :: this
integer :: ierr
deallocate(this%data,stat=ierr)
end subroutine destroy
subroutine printout(this)
class(mytype), intent(inout), target :: this
integer :: ndata,i
ndata = merge(size(this%data),0,allocated(this%data))
write(*,1) ndata,(this%data(i),i=1,ndata)
1 format('mytype object has data(',i0,')',:,' = ',*(f3.1,:,', '))
end subroutine printout
subroutine mytype_print_c(this) bind(C,name='mytype_print_c')
type(c_ptr), intent(inout) :: this
type(mytype), pointer :: fortranclass
call c_f_pointer(this, fortranclass)
call fortranclass%printout()
end subroutine mytype_print_c
subroutine mytype_destroy_c(this) bind(C,name='mytype_destroy_c')
type(c_ptr), intent(inout) :: this
type(mytype), pointer :: fortranclass
call c_f_pointer(this, fortranclass)
if (associated(fortranclass)) then
call fortranclass%destroy()
deallocate(fortranclass)
end if
! Nullify C pointer
this = c_null_ptr
end subroutine mytype_destroy_c
subroutine mytype_init_c(this,ndata,data) bind(C,name='mytype_init_c')
type(c_ptr), intent(inout) :: this
integer(c_int), intent(in), value :: ndata
real(c_float), intent(in) :: data(ndata)
type(mytype), pointer :: fortranclass
integer :: ierr
! In case it was previously allocated
call c_f_pointer(this, fortranclass)
allocate(fortranclass,stat=ierr)
call fortranclass%init(data)
this = c_loc(fortranclass)
end subroutine mytype_init_c
end module mytype_m
that would be bound to an opaque pointer in c++:
#include <iostream>
#include <vector>
using namespace std;
// Fortran interoperability
typedef void* mytype;
extern "C" { void mytype_print_c(mytype self);
void mytype_destroy_c(mytype self);
void mytype_init_c(mytype self, const int ndata, float *data); }
// Class definition
class mytype_cpp
{
public:
mytype_cpp(std::vector<float> data) { mytype_init_c(this,data.size(),data.data()); };
~mytype_cpp() { mytype_destroy_c(this); };
void printout() { mytype_print_c(this); };
};
int main()
{
// Print 8--size
std::vector<float> data {1.,2.,3.,4.,5.,6.,7.,8.};
mytype_cpp obj(data); obj.printout();
return 0;
}
which, with gfortran-10, returns
mytype object has data(8) = 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0
I don't have a chance to test with ifort, but it works seamlessly with gcc, how can this approach not be Fortran standard-compliant?
Posts above inspired the following solution. The idea is to create a type that wraps the actual data array. Then, c_loc/c_f_pointer sequence works fine with a pointer to a scalar object. The data array stored in the type can be safely allocated, along with the array type itself.
MODULE arraytype_m
TYPE, PUBLIC :: arraytype
INTEGER, ALLOCATABLE :: data(:)
END TYPE arraytype
END MODULE arraytype_m
PROGRAM fort_tst
USE iso_c_binding
USE arraytype_m
TYPE(arraytype), POINTER :: a, b
TYPE(C_PTR) :: ptr
ALLOCATE(a)
ALLOCATE(a%data(5))
!! Set to C-style pointer, and then copy back to Fortran pointer.
ptr = c_loc(a)
CALL c_f_pointer(ptr,b)
DEALLOCATE(b%data)
DEALLOCATE(b)
END PROGRAM fort_tst
This works with both Intel and gfortan, and is really a better solution than what I was trying to do.
Special thanks for #Federico for posting the C++/Fortran code that made this solution obvious.
Update : A complete code, which shows how the ptr above can be stored in C.
// C code
typedef void* arraytype;
void allocate_array(arraytype *ptr);
void deallocate_array(arraytype *ptr);
void do_something(arraytype *ptr);
int main()
{
arraytype ptr;
allocate_array(&ptr);
do_something(&ptr);
deallocate_array(&ptr);
return 0;
}
and the corresponding Fortran :
!! Fortran code
MODULE arraytype_mod
TYPE, PUBLIC :: arraytype
DOUBLE PRECISION, POINTER :: data(:)
END TYPE arraytype
END MODULE arraytype_mod
SUBROUTINE allocate_array(ptr) BIND(C,name='allocate_array')
USE iso_c_binding
USE arraytype_mod
TYPE(c_ptr) :: ptr
TYPE(arraytype), POINTER :: a
ALLOCATE(a)
ALLOCATE(a%data(5))
ptr = c_loc(a)
END
SUBROUTINE deallocate_array(ptr) BIND(C,name='deallocate_array')
USE iso_c_binding
USE arraytype_mod
TYPE(C_PTR) :: ptr
TYPE(arraytype), pointer :: a
CALL c_f_pointer(ptr,a)
DEALLOCATE(a%data)
DEALLOCATE(a)
END
SUBROUTINE do_something(ptr) BIND(C,name='do_something')
USE iso_c_binding
USE arraytype_mod
TYPE(c_ptr) :: ptr
TYPE(arraytype), POINTER :: a
CALL c_f_pointer(ptr,a)
a%data = 2.5
WRITE(6,*) a%data
END

Invoking a subroutine where the argument is pointer alias

I am not sure whether this is legal according to Fortran Standard. I have a subroutine which I am calling through overloading. The point here that sometimes I may call the subroutine where I have pointer alias to real.
Please have a look at this complete code.
module mpi_intf_exam
use mpi
interface GLB_SUM
module procedure GLB_SUM_INT
module procedure GLB_SUM_FLT
module procedure GLB_SUM_FLT_INPLACE
end interface
integer :: mpierr
contains
subroutine GLB_SUM_INT(buf, buf_out, n)
implicit none
integer, intent(in) :: n
integer, intent(in) :: buf(n)
integer, intent(out) :: buf_out(n)
call mpi_allreduce( buf, buf_out, n, MPI_INTEGER, MPI_SUM, MPI_COMM_WORLD, mpierr)
end subroutine
subroutine GLB_SUM_FLT(buf, buf_out, n)
implicit none
integer, intent(in) :: n
real, intent(in) :: buf(n)
real, intent(out) :: buf_out(n)
call mpi_allreduce( buf, buf_out, n, MPI_REAL, MPI_SUM, MPI_COMM_WORLD, mpierr)
end subroutine
subroutine GLB_SUM_FLT_INPLACE(buf, n)
implicit none
integer, intent(in) :: n
real, intent(inout) :: buf(n)
call mpi_allreduce( MPI_IN_PLACE, buf, n, MPI_REAL, MPI_SUM, MPI_COMM_WORLD, mpierr)
end subroutine
end module
program test_gather
use mpi_intf_exam
implicit none
integer :: rank, ierr, nranks, n, i
real, allocatable, target :: bufs(:), bufr(:)
real, pointer :: ptr(:)
call mpi_init(ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD, nranks, ierr)
n = 10
allocate(bufs(n))
ptr => bufs
call random_number(ptr)
ptr = ptr*(rank+1)
print*, sum(bufs), rank, "BEF"
call GLB_SUM(ptr,n) !
print*, sum(bufs), rank
end program
My call call GLB_SUM(ptr,n) is meant to call the routine GLB_SUM_FLT_INPLACE. But as you see this subroutine has real dummy argument, while I am invoking it with a real pointer.
It works on IFORT V19.0.5 for this specific example. But is it valid? I could not find what the standard would say about such call.
It is legal to use a pointer in this way.
When a pointer is used in a procedure reference to correspond to a non-pointer dummy argument, the pointer's target is taken to be associated as the actual argument. The pointer itself must be pointer associated. For Fortran 2018 you can see 15.5.2.3.
In the case of the question, each specific subroutine for the generic GLB_SUM has no pointer argument.
There is no problem passing a pointer to a non-pointer dummy argument. Fortran does not require any dereferencing you may know from other programming languages. The subroutine just receives the array that is the target of the pointer. It is OK.

How to write a function which returns an allocated shared memory array in fortran?

I'd like to create a subroutine which takes an ALLOCATABLE array and returns an MPI shared memory array.
I have a bunch of code written in MPI where we use ALLOCATABLE arrays. Now many of these arrays are identical across nodes so storing them in some type of shared memory object would be best. Now I found this example (MPI Fortran code: how to share data on node via openMP?) which works as a standalone code but when I try to implement it as a subroutine I get a Segmentation Fault from the C_F_POINTER call.
The driver routine looks like
PROGRAM TEST_SUBROUTINE
! Libraries
USE MPI
IMPLICIT NONE
! Variables
INTEGER :: ier, myid, numprocs
INTEGER :: myid_shar, numprocs_shar
INTEGER :: MPI_COMM_SHARMEM, win_a
DOUBLE PRECISION, POINTER :: A(:)
! Code
CALL MPI_INIT(ier)
CALL MPI_COMM_RANK( MPI_COMM_WORLD, myid, ier )
CALL MPI_COMM_SIZE( MPI_COMM_WORLD, numprocs, ier)
myid_shar=0
CALL MPI_COMM_SPLIT_TYPE(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, MPI_COMM_SHARMEM, ier)
CALL MPI_COMM_RANK( MPI_COMM_SHARMEM, myid_shar, ier )
CALL MPI_COMM_SIZE( MPI_COMM_SHARMEM, numprocs_shar, ier)
CALL mpialloc_1d_dbl(A,numprocs_shar,myid_shar,0,MPI_COMM_SHARMEM,win_a)
A(myid_shar+1) = myid_shar
CALL MPI_WIN_FENCE(0, win_a, ier)
IF (myid == 0) THEN
PRINT *,A(1)
PRINT *,A(2)
PRINT *,A(3)
PRINT *,A(4)
END IF
! FREE Window
CALL MPI_WIN_FENCE(0, win_a, ier)
CALL MPI_BARRIER(MPI_COMM_SHARMEM, ier)
CALL MPI_WIN_FREE(win_a,ier)
! FREE MPI_COMM_SHARMEM
CALL MPI_BARRIER(MPI_COMM_SHARMEM, ier)
CALL MPI_COMM_FREE(MPI_COMM_SHARMEM,ier)
! END MPI
CALL MPI_BARRIER(MPI_COMM_WORLD, ier)
CALL MPI_FINALIZE(ier)
END PROGRAM TEST_SUBROUTINE
The subroutine looks like (note I've tried playing with the array variable but without much luck)
SUBROUTINE mpialloc_1d_dbl(array,n1,subid,mymaster,share_comm,win)
! Libraries
USE MPI
USE ISO_C_BINDING
IMPLICIT NONE
! Arguments
DOUBLE PRECISION, POINTER, INTENT(inout) :: array(:)
INTEGER, INTENT(in) :: n1
INTEGER, INTENT(in) :: subid
INTEGER, INTENT(in) :: mymaster
INTEGER, INTENT(inout) :: share_comm
INTEGER, INTENT(inout) :: win
! Variables
INTEGER :: disp_unit, ier
INTEGER :: array_shape(1)
INTEGER(KIND=MPI_ADDRESS_KIND) :: window_size
TYPE(C_PTR) :: baseptr
ier = 0
array_shape(1) = n1
disp_unit = 8_MPI_ADDRESS_KIND
window_size = 0_MPI_ADDRESS_KIND
IF (subid == mymaster) window_size = INT(n1,MPI_ADDRESS_KIND)
CALL MPI_BARRIER(share_comm, ier)
CALL MPI_WIN_ALLOCATE_SHARED(window_size, disp_unit, MPI_INFO_NULL, share_comm, baseptr, win ,ier)
IF (subid /= mymaster) CALL MPI_WIN_SHARED_QUERY(win, 0, window_size, disp_unit, baseptr, ier)
CALL C_F_POINTER(baseptr, array, array_shape)
CALL MPI_WIN_FENCE(0, win, ier)
RETURN
END SUBROUTINE mpialloc_1d_dbl
What I'd like to have is a subroutine which behaves similar to a simple ALLOCATE statement, returning the shared memory POINTER and the window variables for FENCE calls.
OK so the error here has to do with calling Fortran 90 style subroutines. See this link for a partial explanation (http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html#8) Now in the above example I'd actually just put the subroutine at the end of the program. This has the effect of acting as an implicit interface statement (at least in with GFORTRAN and INTEL compilers). So my Pseudo-code worked fine, however in my production code the subroutine added as part of a general use library that many other pieces of code called. If I linked my Pseudo-code to this library which was a copy-paste of the subroutine in the Pseudo-code, the code would crash as in the production code. However, if I added an INTERFACE block everything worked fine.
So where does that leave me? Well for 'reasons' I didn't want to write yet another specialized module but it seems as if I will have to anyway and put all the various shared memory subroutines in it. The other option is to add interface blocks to every bit of shared memory allocating sub code (blagh).
Here's the fixed code, but you need to compile the subroutine and program seperately and link to see the effect of having / not having the INTERFACE block.
Main program:
PROGRAM TEST_SUBROUTINE
! Libraries
USE MPI
IMPLICIT NONE
INTERFACE
SUBROUTINE mpialloc_1d_dbl(array,n1,subid,mymaster,share_comm,win)
DOUBLE PRECISION, POINTER, INTENT(inout) :: array(:)
INTEGER, INTENT(in) :: n1
INTEGER, INTENT(in) :: subid
INTEGER, INTENT(in) :: mymaster
INTEGER, INTENT(inout) :: share_comm
INTEGER, INTENT(inout) :: win
END SUBROUTINE mpialloc_1d_dbl
END INTERFACE
! Variables
INTEGER :: ier, myid, numprocs
INTEGER :: myid_shar, numprocs_shar
INTEGER :: MPI_COMM_SHARMEM, win_a
DOUBLE PRECISION, POINTER :: A(:)
! Code
CALL MPI_INIT(ier)
CALL MPI_COMM_RANK( MPI_COMM_WORLD, myid, ier )
CALL MPI_COMM_SIZE( MPI_COMM_WORLD, numprocs, ier)
myid_shar=0
CALL MPI_COMM_SPLIT_TYPE(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, MPI_COMM_SHARMEM, ier)
CALL MPI_COMM_RANK( MPI_COMM_SHARMEM, myid_shar, ier )
CALL MPI_COMM_SIZE( MPI_COMM_SHARMEM, numprocs_shar, ier)
CALL mpialloc_1d_dbl(A,numprocs_shar,myid_shar,0,MPI_COMM_SHARMEM,win_a)
A(myid_shar+1) = myid_shar
CALL MPI_WIN_FENCE(0, win_a, ier)
IF (myid == 0) THEN
PRINT *,A(1)
PRINT *,A(2)
PRINT *,A(3)
PRINT *,A(4)
END IF
! FREE Window
CALL MPI_WIN_FENCE(0, win_a, ier)
CALL MPI_BARRIER(MPI_COMM_SHARMEM, ier)
CALL MPI_WIN_FREE(win_a,ier)
! FREE MPI_COMM_SHARMEM
CALL MPI_BARRIER(MPI_COMM_SHARMEM, ier)
CALL MPI_COMM_FREE(MPI_COMM_SHARMEM,ier)
! END MPI
CALL MPI_BARRIER(MPI_COMM_WORLD, ier)
CALL MPI_FINALIZE(ier)
END PROGRAM TEST_SUBROUTINE
Subroutine:
SUBROUTINE mpialloc_1d_dbl(array,n1,subid,mymaster,share_comm,win)
! Libraries
USE MPI
USE ISO_C_BINDING
IMPLICIT NONE
! Arguments
DOUBLE PRECISION, POINTER, INTENT(inout) :: array(:)
INTEGER, INTENT(in) :: n1
INTEGER, INTENT(in) :: subid
INTEGER, INTENT(in) :: mymaster
INTEGER, INTENT(inout) :: share_comm
INTEGER, INTENT(inout) :: win
! Variables
INTEGER :: disp_unit, ier
INTEGER :: array_shape(1)
INTEGER(KIND=MPI_ADDRESS_KIND) :: window_size
TYPE(C_PTR) :: baseptr
ier = 0
array_shape(1) = n1
disp_unit = 8_MPI_ADDRESS_KIND
window_size = 0_MPI_ADDRESS_KIND
IF (subid == mymaster) window_size = INT(n1,MPI_ADDRESS_KIND)
CALL MPI_BARRIER(share_comm, ier)
CALL MPI_WIN_ALLOCATE_SHARED(window_size, disp_unit, MPI_INFO_NULL, share_comm, baseptr, win ,ier)
IF (subid /= mymaster) CALL MPI_WIN_SHARED_QUERY(win, 0, window_size, disp_unit, baseptr, ier)
CALL C_F_POINTER(baseptr, array, array_shape)
CALL MPI_WIN_FENCE(0, win, ier)
RETURN
END SUBROUTINE mpialloc_1d_dbl

When do Fortran module allocatable arrays go out of scope when loaded as a shared library?

I am calling a fortran subroutine from R as part of a more complex optimization problem. Currently, the subroutine is self-contained -- with an input of current parameter values and an output of function evaluation and gradient terms. I now wish to initialize an allocatable array as a shared module variable (prior to the optimization) that will be used (but not modified) by the subroutine during optimization.
In this context, when would the shared allocatable array go out of scope or be deleted?
A naive reading of the memory management section of Fortran wikibook suggests that module variables should persist (possibly even after a program has been executed).
A number of sources I've read indicate that an allocatable array will be deallocated automatically when it goes out of scope. Does this happen for module variables as well and when would this happen?
I've found a number of related questions but I haven't been able to place them in context of both module variables and shared library loading.
Edit:
A minimal example of the fortran module. The allocated array works as expected in a fortran program. In practice, both init() and eval() will be wrapped by R functions (init_wrap() and eval_wrap()) and called from R. I want to confirm that the allocated variable y is guaranteed not to go out of scope or get deleted while test_module is loaded as a shared library.
module test_module
double precision, allocatable, dimension(:,:) :: y
contains
subroutine init() bind(C, name = "init_")
if (.not. allocated(y) ) then
allocate(y(1,1))
end if
y = 1
end subroutine init
subroutine eval(x, z) bind(C, name = "eval_")
double precision, intent(in) :: x
double precision, intent(out) :: z
z = x + y(1,1)
end subroutine eval
end module test_module
! Program added for testing purposes only,
! module is used as shared library
program test_program
use test_module
double precision :: x, z
! Initialize allocatable array
call init()
! Use allocatable array during optimization
x = 1
call eval(x, z)
x = 2
call eval(x, z)
print *, z
end program test_program
Edit 2:
I've created a skeleton package on github that models how I'm using fortran code: https://github.com/ssokolen/fortran.test
The following R code works as I need it to (the allocated array keeps its value between eval_wrap() calls), but I'm still hoping to get a definitive answer on when an allocated module variable would go out of scope when loaded as a shared library (or an answer that states that there is no general behaviour).
library(devtools)
install_github('ssokolen/fortran.test')
library(fortran.test)
init_wrap()
eval_wrap(1)
eval_wrap(2)
Dynamically loaded libraries are beyond Fortran standard. What happens indicated by the whole "processor" which is the complex of Fortran compiler, operating system, linker and so on.
In Linux and other POSIX operating systems, if you unload the library from memory, it will go out of scope. This simple test case demonstrates that:
module test_module
double precision, allocatable, dimension(:,:) :: y
contains
subroutine init() bind(C, name = "init_")
print *, "init_"
if (.not. allocated(y) ) then
allocate(y(1,1))
end if
y = 1
end subroutine init
subroutine eval(x, z) bind(C, name = "eval_")
double precision, intent(in) :: x
double precision, intent(out) :: z
if (.not. allocated(y) ) error stop("not allocated!")
z = x + y(1,1)
print*, "success"
end subroutine eval
end module test_module
And C calling the library:
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
int main(){
void *handle = dlopen("/home/lada/f/testy/stackoverflow/dltest.so",RTLD_NOW);
void (*init_)() = (void (*)())dlsym(handle, "init_");
init_();
double x=0,y=0;
void (*eval_)(double x, double z) = (void (*)())dlsym(handle, "eval_");
eval_(x, y);
dlclose(handle);
handle = dlopen("./dltest.so",RTLD_NOW);
eval_ = (void (*)())dlsym(handle, "eval_");
eval_(x, y);
return 0;
}
And run:
> gfortran -shared -fPIC -o dltest.so dltest.f90
> gcc -ggdb dltest.c -ldl
> ./a.out
init_
success
ERROR STOP not allocated!
The array is no longer allocated after dlclose() and dlopen(). You must make sure that R will not unload the library from memory.

Calling a Fortran subroutine which uses module functions from R

I am trying to understand how to call Fortran from R. I'm ok with calling a code containing only a subroutine. However I would like to call a Fortran code that contains a module with functions used in a subroutine. Here is a simple example, that I compiled, but wasn't able to call from R using the command "dyn.load":
module forme
implicit none
public :: autoc
contains
function autoc(x,k) result(f_res)
double precision, intent(in) :: x
integer, intent(in) :: k
double precision :: f_res
f_res=x+k
return
end function autoc
end module forme
subroutine invdeux(n,answer)
use forme
implicit none
integer, intent(in):: n
double precision, intent(out):: answer
double precision :: nreal
nreal=n
answer=1/nreal
answer=autoc(answer,n)
end subroutine invdeux

Resources