Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Cannot access matrix assigned in c from Fortran

cathleenmcguiness
486 Views

Hi,

I need to do some interfacing between c and Fortran and am having huge problems with anything that isn't just a single value (i.e., I cannot seem to pass matrices and arrays properly).

I have a system with a Fortran main using a c library. The Fortran main calls the c lib, which is supposed to return some data previously received from another c program, via a socket. Everything in the c parts looks fine (i.e. the socket communication is not the problem) but I can't seem to properly pass the data from c to Fortran (Fortran to c is no issue). Either the data appears to be passed correctly but Fortran then just randomly freezes or the whole thing crashes as the c function returns (*** glibc detected *** double free or corruption(fasttop): 0x0804a008 ***Aborted...).

My Fortran part looks like this:

INTERFACE
    SUBROUTINE recvDataFromC(sockID, nU, nC, timeStep, cAddress)     BIND (C, NAME = "recvDataFromC")

      USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double, c_int, c_intptr_t

      INTEGER (KIND = c_int), VALUE :: sockID
      INTEGER (KIND = c_int)           :: nU, nC
      REAL (KIND = c_double)          :: timeStep
      INTEGER (KIND = c_intptr_t)     :: cAddress
      
    END SUBROUTINE recvDataFromC
  END INTERFACE

 To call the c function and then use data, I do the following (and if someone can point me to a better way of doing this (if there is one), that would be divine):

  CALL recvDataFromC(sockID_, n_Unknowns_, n_Cells_, timeStep_, cAddress)

  ALLOCATE (serialisedMatrix(n_Unknowns_ * n_Cells_))

  c_matrix = TRANSFER (cAddress, c_matrix)
  CALL C_F_POINTER(c_matrix, serialisedmatrix, [n_Unknowns_*n_Cells_])

where things are declared like this:

  TYPE, BIND (C) :: matrixElement
    REAL (KIND = c_double) :: val
  END TYPE matrixElement

  TYPE (matrixElement), ALLOCATABLE, TARGET :: serialisedMatrix(:)
  TYPE (c_ptr) :: c_matrix
  INTEGER (KIND = c_intptr_t) :: cAddress

  INTEGER (KIND = c_int) :: n_Unknowns_, n_Cells_
  REAL (KIND = c_double) :: timeStep_

From here, I expect to be able to access serialisedMatrix%val where val ranges from 1 to n_Unknowns_*n_Cells_.

The c function looks like this:

  void recvDataFromClient(int sockID, int *nU, int *nC, double *timeStep, intptr_t *cAddress) {

    double *serialisedMatrix;

     *** read values for nU and nC from socket ***

    if ( ( serialisedMatrix = malloc(sizeof(double) * (*nU) * (*nC))) == NULL) {

      printf("\n malloc() failed.");

      exit(1);

    }

     ... read data from socket and put it into serialisedMatrix ...

     *cAddress = serialisedMatrix;

  }

sockID is received without problems, likewise is n_Unknowns_ , n_Cells_ and timeStep_ always returned without problems. The c address however, doesn't seem to work the way I want it to.

I would like to have the last line of recvDataFromC as

  cAddress = (intptr_t*) serialisedMatrix;

but that causes the call to C_F_POINTER to fail.

Fortran part is compiled with (IFORT) 12.1.0 20110811 on a CentOS 5.x system using these flags:

  -check all -debug all -traceback -fpe0 -fp-model precise -ftz -ftrapuv -fno-alias -I$(CLIBS)

wich CLIBS being the path to where the c part is compiled.

c part is compiled with

  gcc -c -g -fPIC -Wall

I can't run this in idb, it claims that the source files don't exist and lists the process as deleted. gdb, also returns no useful information.

If anyone can help with how to accurately pass a matrix from c to Fortran I would be ultra mega thankful (if it helps, I already know the size of the matrix in advance), I've been fiddling around with this for days but getting nowhere.

Thanks!

/Cathleen

0 Kudos
4 Replies
IanH
Honored Contributor II
486 Views
The second argument to C_F_POINTER needs to be a Fortran pointer - i.e. the declaration for serialisedmatrix should be [fortran]TYPE (matrixElement), POINTER :: serialisedMatrix(:)[/fortran] I am ... a little surprised ... that the compiler doesn't diagnose this. The memory for that pointer is being allocated by your C function, so you should get rid of the allocate statement that leads the call to C_F_POINTER. Because C has allocated the memory you will need to have some way of getting C to free the memory, perhaps by passing the c address of serialised matrix back to C later on (or if you are lazy like me, perhaps by just calling C's free from Fortran with that address). On the Fortran side you have the pointer cAddress coming back as an integer(C_INTPTR_T). Unless you are actually planning to do pointer arithmetic, it is simpler to make that argument a C_PTR and then you can eliminate the c_matrix temporary and all that transfer nonsense. On the C side, the cAddress parameter would more naturally seem to be a double ** - "a pointer to a pointer to double", equivalent to "a pointer to an array of double". I don't think your "I would like to..." line makes sense - it needs a * in front of cAddress (as you have in the main body example of C code) given the indirection on the parameter.
0 Kudos
cathleenmcguiness
486 Views
Thanks for your help, I am a bit confused though: From what I gather, cAddress is now a c_ptr, yet you're also suggesting that the memory for serialisedMatrix is allocated in the c function but I don't see how? Letting the last argument of recvDataFromC being a double** leads to ** glibc detected *** ./main.exe: free(): invalid next size (fast): 0x00000000057d2ac0 *** on return. I can however return a double * it seems, if I just serialise the matrix on the c-side (which is fine, I don't mind doing that). The question now then, is how to extract data from the returned cAddress (which is now of type c_ptr)?
0 Kudos
JVanB
Valued Contributor II
486 Views
It may have made it easier if you had written out your code with changes as in your first post. As it is we have to guess where you made the latest mistake. I assume: In the C code, the only change you made was to chamge the declaration of cAddress to double **cAddress. Then the C code should be OK, I only point out that memory was allocated when you said serialisedMatrix = malloc(sizeof(double) * (*nU) * (*nC)). In the Fortran code, you changed the declaration of cAddress to type(C_PTR) cAddress both in the interface body and the code itself. Then also change the declaration of serialisedMatrix to real(C_DOUBLE), pointer :: serialisedMatrix(:) and then simply call C_F_POINTER(cAddress,serialisedMatrix,[n_Unknowns_*n_Cells_]) should allow you to extract the data as desired.
0 Kudos
cathleenmcguiness
486 Views
There's a serialisedMatrix on the Fortran side and there's one on the c side, hence the confusion. Anyways, found a solution elsewhere, things are stable and functional now. Thanks!
0 Kudos
Reply