作为共享库加载时,Fortran 模块可分配数组何时会超出范围?

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

我正在从 R 调用一个 fortran 子例程,作为更复杂的优化问题的一部分。目前,该子程序是独立的——输入当前参数值,输出函数评估和梯度项。我现在希望将一个可分配数组初始化为共享模块变量(在优化之前),在优化期间将由子例程使用(但不修改)。

在这种情况下,共享可分配数组何时会超出范围或被删除?

对 Fortran wikibook memory management 部分的天真阅读建议模块变量应该保留(甚至可能在程序执行后)。

我读过的许多 sources 指出可分配数组在超出范围时将自动释放。模块变量也会发生这种情况吗?什么时候会发生?

我找到了 number of related questions 但我无法将它们放在模块变量和共享库加载的上下文中。

编辑:

fortran 模块的最小示例。分配的数组在 Fortran 程序中按预期工作。实际上,init()eval() 都将被 R 函数(init_wrap()eval_wrap())包装并从 R 调用。我想确认分配的变量 y 保证不会超出范围或在 test_module 作为共享库加载时被删除。

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

编辑 2:

我在 github 上创建了一个框架包,模拟了我如何使用 Fortran 代码:https://github.com/ssokolen/fortran.test

以下 R 代码按我的需要工作(分配的数组在 eval_wrap() 调用之间保持其值),但我仍然希望得到关于分配的模块变量何时消失的明确答案作为共享库加载时超出范围(或说明没有一般行为的答案)。

library(devtools)
install_github('ssokolen/fortran.test')
library(fortran.test)
init_wrap()
eval_wrap(1)
eval_wrap(2)

动态加载的库超出了 Fortran 标准。整个 "processor" 表示发生了什么,它是 Fortran 编译器、操作系统、链接器等的复合体。

在 Linux 和其他 POSIX 操作系统中,如果从内存中卸载库,它将超出范围。这个简单的测试用例表明:

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

C调用库:

#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>

int main(){

  void *handle = dlopen("/home/lada/f/testy/Whosebug/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;

}

和运行:

> gfortran -shared -fPIC -o dltest.so dltest.f90
> gcc -ggdb  dltest.c -ldl
> ./a.out 
 init_
 success
ERROR STOP not allocated!

数组在dlclose()dlopen()之后不再分配。您必须确保 R 不会从内存中卸载库。