3
votes

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)
1
"possibly even after a program has been executed" No, that is not true. But compilers should now treat module variables as save so they do not go out of scope during the program run. If you somehow unload and then load again a shared library, that could cause anything. So the question is, are you really somehow unloading the shared library?Vladimir F
@francescalus I will add a minimal fortran example shortly, but I would need to create a whole R package to make it complete and illustrate the loading process. Please let me know if you think that's necessary.ssokolen
@VladimirF Thanks for the clarification. No, the library should not be unloaded during program execution. Is loading and unloading a shared library identical to running a program from the point of view of the module?ssokolen
The Fortran standard does not speak about this, it is an implementation detail. But it may be important in practice.Vladimir F
I disagree. The Fortran standard does have this section: "16.6.6 Events that cause variables to become undefined" and that situation is NOT listed. j3-fortran.org/doc/year/10/10-007.pdfJack

1 Answers

1
votes

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.