Home > Mobile >  How to fix abrupt program exit in Cython interface to Fortran
How to fix abrupt program exit in Cython interface to Fortran

Time:05-24

Short version

I have old Fortran code I am trying to access by wrapping it through a Cython extension module (have done that already for other Fortran libraries). The code works when called from inside Fortran, but when accessed via the Cython wrapper it forces the program to stop with either exit code -1073741819 (0xC0000005) or -1 and no other message. What is more: this crash happens at random times: sometimes the Fortran code manages to return, but the Python script still crashes just after.

I am not sure where the problem can be, since the Fortran subroutine has a simple signature signature which should make the interface relatively simple. More details below (trimmed down as much as I could).

Ideas are greatly appreciated!

Cython setup

Relevant files:

  • mylib.lib: Original Fortran library code
  • wrapper.h: Header file for Cython
  • cython_module.pyx: Cython code
  • wrapper.f90: Wrapper code for binding the Fortran library to C code
  • wrapper.o: Obtained compiling the Fortran wrapper using the ifort compiler. Command: ifort wrapper.f90 /c /o wrapper.o /O3
  • setup.py: Compilation setup

Here is the compilation setup file. As I mentioned before, I have used a nearly identical script for other Fortran-Cython interfaces, so it should be fine, but...

from distutils.core import setup
from Cython.Distutils import build_ext
from Cython.Distutils import Extension  # Adds functionality to the standard distutils.extension.Extension
import Cython.Compiler.Options
from numpy import get_include

module_name = 'cython_module'
cython_interface = 'cython_module.pyx'
link_args = ['wrapper.o', 'mylib.lib']
fortran_libs = [  # Additional library directories for .lib files
    r'C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.2.254\windows\compiler\lib\intel64_win'
]
# Generate an additional HTML file with some feedback on optimization quality
Cython.Compiler.Options.annotate = True

ext_modules = [Extension(
    module_name,
    [cython_interface],
    # I need this for using Complex numbers in other library functions not shown here
    define_macros=[("CYTHON_CCOMPLEX", "0")],
    extra_compile_args=['/O2'],
    extra_link_args=link_args,
    library_dirs=fortran_libs,
    cython_directives={'language_level': "3"}
)]

setup(name='cython_module',
      cmdclass={'build_ext': build_ext},
      # Needed if building with NumPy.
      include_dirs=[get_include()],
      ext_modules=ext_modules)

Cython code

Contents of cython_module.pyx:

import numpy as np

cdef extern from "<complex.h>":  # You can ignore this
    ctypedef struct _Dcomplex

cdef extern from "wrapper.h" nogil:
    void py_indexh(double* a, double* b, long long* m, long long* n);

def indexh(double px, double py):
    cdef long long[:] m = np.empty((1000,), dtype=np.int64, order='F')
    cdef long long[:] n = np.empty((1000,), dtype=np.int64, order='F')
    py_indexh(&px, &py, <long long*> m[0], <long long*> n[0])
    return m, n

And the header file (for completeness only, very simple):

#include <complex.h>
extern void py_indexh(double* a, double* b, long long* m, long long* n);

Fortran wrapper

I am only showing the Fortran wrapper, and not the entire library subroutine body, since it is fairly long and convoluted and it clearly works when called from Fortran itself. Here it is:

subroutine py_indexh(a, b, M, N) bind(c)
    use iso_c_binding, only: c_float, c_double, c_int, c_int32_t, c_int64_t, c_double_complex, c_bool
    implicit none

    interface
        subroutine indexh(a, b, M, N)
            real(8),        intent(in)  :: a, b
            integer(8),     intent(out) :: M(1000), N(1000)
        end subroutine indexh
    end interface

    real(c_double),        intent(in)   :: a, b
    integer(C_INT64_T),     intent(out) :: M(1000), N(1000)

    print *, 'Entering indexh from Fortran...'
    call indexh(a, b, M, N)
    print *, 'Exitted indexh from Fortran...'
end subroutine py_indexh

The issue / output

The code compiles with no major issues. Only this warnning is shown, which I believe is related to how the .lib file was compiled. Please let me know if I should worry more about this, it caused no harm in other Cython wraps of Fortran I have done: LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library

When importing the cython module and calling cython_module.indexh(3e-3, 4e-3), it silently crashes with either exit code -1 or -1073741819 (0xC0000005). Before crashing, however, the Fortran subroutine is successfully entered, since 'Entering indexh from Fortran...'is printed on console. Sometimes, even the second print 'Exitted indexh from Fortran...' is shown, which should indicate that the subroutine was finished. However, regardless of whether one or the two prints are shown, the program crashes just after the function call.

Just to clarify, there is no weird multithreaded business or anything unusual in the body of indexh(...), only a lot of nested loop action.

Again, any help or ideas are greatly appreciated! Please let me know if there is any more information I can show.

CodePudding user response:

<long long*> m[0], <long long*> n[0]

This casts the first element of m and n (i.e. an integer) to a long long*.

You want

&m[0], &n[0]

to get the address of the first element.

Casts are often just a way of shutting the compiler up when it's telling you about a bug.

  • Related