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

Mapping Shared Memory from C++

Merik_G_
Beginner
1,869 Views

I'm creating a shared memory file mapping in a DLL in C++ and assign it to SM01

extern "C" {

__declspec (dllexport) char *SM01 = NULL;

}

I can use this in another C++ DLL by doing this

extern "C" __declspec(dllimport) char *SM01;

#define abc1 (*(int *)(SM01 + 4))

I can assign abc1 to a value in shared memory.

I would like to do the equivalent in a FORTRAN DLL but it's not working.  Something like this.

USE, INTRINSIC :: ISO_C_BINDING

IMPLICIT NONE

TYPE(C_PTR), BIND(C) :: SM01

SUBROUTINE FORTRAN_LIBRARY()

INTEGER abc1

EQUIVALENCE (abc1,SM01(4))

abc1 = 555

END

 

 

 

0 Kudos
23 Replies
Xiaoping_D_Intel
Employee
1,616 Views

https://software.intel.com/en-us/node/678433 provides an example on how to interoperate with C pointer.

For your case:

USE, INTRINSIC :: ISO_C_BINDING
CHARACTER,POINTER::ar(:)=> NULL()
INTEGER,PARAMETER :: n_elements = 4
!DIR$ ATTRIBUTES DLLIMPORT :: SM01
type(c_ptr) :: SM01

call C_F_POINTER (sm01,ar,[n_elements])
ar(4) = 'c'

 

Thanks,

Xiaoping Duan

Intel Customer Support

 

 

0 Kudos
Merik_G_
Beginner
1,616 Views

I'm still having 2 problems.

1.  I get this even though I'm linking in the library that defines the symbol.  I'm already linking the library for the C part.

error LNK2001: unresolved external symbol __imp_SM01

2. I'm trying to map the whole block of 2000 characters (as allocated in C) and assign different variable types in Fortran.  I would also like to see the same values I assigned in C++ in Fortran.  As shown by the following, I'm trying to read the value set in C++ which was assigned at position 4.  I guessing in Fortran it will be position 5. 

INTEGER abc1

EQUIVALENCE (abc1,SM01(5))

Thanks 

0 Kudos
Xiaoping_D_Intel
Employee
1,616 Views

1. Please check if your C++ DLL correctly exports the symbol by command "dumpbin/exports <dllname>.lib".

2. EQUIVALENCE can't be used for object with DLLIMPORT attribute. The default lower bound of fortran array is 1 and of C++ is 0 so it is 5 in fortran.

 

Thanks,

Xiaoping Duan

Intel Customer Support

0 Kudos
Merik_G_
Beginner
1,616 Views

Number 1 is fixed using your solution to look at the dump.  The buffer is exported as _SM01 so now the code looks like this.

USE, INTRINSIC :: ISO_C_BINDING

IMPLICIT NONE

 

CHARACTER,POINTER::ar(:)=> NULL()

INTEGER,PARAMETER :: n_elements = 4

!DIR$ ATTRIBUTES DLLIMPORT :: _SM01

TYPE(C_PTR) :: SM01

call C_F_POINTER (SM01,ar,[n_elements])

ar(4) = 'c'

 But this crashed when setting ar(4) = 'c' In the debugger I see SM%PTR is 0 and ar(4) as undefined address.

Furthermore, is there a way to map the memory of 2000 bytes to different types?  I don't want to map the whole memory, just a portion.  Let say:

C     Map ar(1:4)

INTEGER A

C     Map ar(5:8)

INTEGER B

C     Map ar(9:88)

INTEGER C(1:20)

C     skip to the 200th byte to Map a real8 (201:208)

REAL*8 D

 

 

 

 

 

 

0 Kudos
Merik_G_
Beginner
1,616 Views

I have the solution but one last question.  Below is the code.  Is there a way to not create module mymod2 and just express the DLL import in the subroutine?  When I move it, I get the following error: If any bind-entity in a bind-stmt is an entity-name, the bind-stmt shall appear in the specification part of a module

module mymod2

use, intrinsic :: ISO_C_BINDING

 

IMPLICIT NONE

 

CHARACTER,POINTER::ARR01(:)=> NULL()

type(C_PTR), bind(C) :: SM01

!DIR$ ATTRIBUTES DLLIMPORT :: SM01

END

 

SUBROUTINE FORTRAN_LIBRARY2()

USE, INTRINSIC :: ISO_C_BINDING

USE mymod2

IMPLICIT NONE

 

integer*4 ,pointer :: F0

integer*4 ,pointer :: F1

real*4, pointer :: F2

integer*4, pointer :: F3(:)

 

call C_F_POINTER (SM01, ARR01, [2000])

call C_F_POINTER(C_LOC(ARR01(1)), F0)

call C_F_POINTER(C_LOC(ARR01(5)), F1)

call C_F_POINTER(C_LOC(ARR01(9)), F2)

call C_F_POINTER(C_LOC(ARR01(13)), F3,[4])

 

F0 = 673

F1 = 544

f2 = 66.7

END

0 Kudos
Steve_Lionel
Honored Contributor III
1,616 Views

I don't get any errors compiling this source in 17.0.2.  But no, you can't declare a variable with BIND(C) except as a module variable. The DLLIMPORT is not the issue (though since you can't declare an "external" variable outside of a module, you can't DLLIMPORT it either. You can DLLIMPORT a COMMON, but using a BIND(C) variable is the better approach.

0 Kudos
Merik_G_
Beginner
1,616 Views

OK, thanks.  I'll stick with creating a module.

Another question.  Does FORTRAN care how the memory is mapped with pointer as shown above?  Is there an latency when an INTEGER or ARRAY is not bound on 4 or 8 bytes. 

Here is an example where F0 isn't padded to keep the memory bound to 4 bytes:

LOGICAL*1, POINTER :: F0

INTEGER*4, POINTER :: F1

REAL*8, POINTER :: F2

INTEGER*4, POINTER :: F3(:)

 

call C_F_POINTER (SM02, SMARR02, [2000])

call C_F_POINTER(C_LOC(SMARR02(1)), F0)

call C_F_POINTER(C_LOC(SMARR02(2)), F1)

call C_F_POINTER(C_LOC(SMARR02(6)), F2)

call C_F_POINTER(C_LOC(SMARR02(14)), F3,[10])

  

 

0 Kudos
Merik_G_
Beginner
1,616 Views

I'll answer my own question. Memory must be aligned to obtain the fastest possible read.  A CPU always reads word size therefore when accessing misaligned data, multiple reads may occur. 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,616 Views

The default array indexing for Fortran is 1-based

SMARR02(1), is the 1st element (when default allocated) there is no 0'th element
You could specify

integer(1) :: SMARR02(0:1999)

if you want a 0-based array of 2000 bytes

>>an example where F0 isn't padded to keep the memory bound to 4 bytes

F0 is a pointer to s logical(1), its (the pointer) size is the size of the machine word (4 or 8 bytes), what it points to would be arbitrarily byte aligned

F3 is different in that it is an array descriptor than can point to an arbitrarily sized array (blob) of 4-byte integers (which could potentially be arbitrarily byte aligned, though you wouldn't normally want to do so).

Jim Dempsey

0 Kudos
Merik_G_
Beginner
1,616 Views

Is quad precision working with Fortran?

REAL*16 , POINTER :: F0

 

call C_F_POINTER (SM02, SMARR02, [2000])

call C_F_POINTER(C_LOC(SMARR02(1)), F0)

 

F0 = 11.22

Visual Studio crashes.

REAL*8 works fine.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,616 Views

Which statement causes the crash?

What is the C_LOC of F0 following its C_F_POINTER (both cases)

Jim Dempsey

0 Kudos
Merik_G_
Beginner
1,616 Views

F0 = 11.22 is causing the crash. F0 crashes VS due to trying to get its value.

C_LOC(SMARR02(1)) is pointing to a shared memory created in C++

Do you mean loc(F0)?  This changes every time since the memory is allocated on the fly.

 

 

 

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,616 Views

When you say "crashes VS", do you mean when attempting to see the value in the debugger? I recall there were debugger issues with REAL(16) in the past. Does the program run without debugging?

0 Kudos
Merik_G_
Beginner
1,616 Views

Yes, the crash is only in the debugger.  In release, it does run but it changes the value to 0.  I had previously set it to 25.6 using a "long double" on the C++ side. 

0 Kudos
Steve_Lionel
Honored Contributor III
1,616 Views

Which compiler version are you using? Would you please include a short but complete program that demonstrates the problem?

0 Kudos
Merik_G_
Beginner
1,616 Views

I'm using VS 2015 integrated with Intel® Parallel Studio XE 2016 building in Release 64bit

CExecutable is the executable that runs.  This is setting the initial values and printing them, Calls Fortran, and then prints values again.

FortranLibrary is library linked into CExecutable.  FortranLibrary.f sets new values.

PSIM is a C++ that creates the Shared Memory

 

 

 

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,616 Views

When I run this, the call to CreateFileMapping in PSIM's DllMain fails, and GetLastError returns 5 (Access Violation). I am not familiar with this API and my study of the MSDN description doesn't suggest why this fails. I did puzzle over your explicitly specifying a handle value instead of the constant INVALID_HANDLE_VALUE, though the effect appears to be the same. This is as far as I got.

0 Kudos
Merik_G_
Beginner
1,616 Views

I'm guessing you need admin privileges to create global memory.  Try changing  

L"Global\\SM01" to  L"Local\\SM01"

and

L"Global\\SM02" to  L"Local\\SM02"

 

0 Kudos
garylscott1
Beginner
1,616 Views

Of possible (but remote) interest, here is an easy method of creating a shared memory buffer using file mapping, without need for a DLL.  You can share across separate processes (you'll need your own synchronization method, not too hard to do), threads, DLLs, even separate machines with only minor tweaks and additional effort.  This was written in ~1999 for DVF or CVF and may need some tweaks for IVF but that should be fairly minor.  Overhead will average in the nanoseconds as this prevents page swapping as-is (can be easily changed).

http://www.fortranlib.com/ShareBufferWin32.f90

I could provide an update (IVF compatibility, modernize, etc.) if there's any interest.

0 Kudos
Steve_Lionel
Honored Contributor III
1,389 Views

Ok, I changed the Global to Local. Got this:

Values: 0 0 0 0
Values: 65 5345 55.5 25.6
Values: 673 544 23.43 0

The zero is because you declared this item REAL*16 in Fortran but "long double" in C. These are not the same type - at least in MSVC, long double is the same as double.  MSDN says:

Previous 16-bit versions of Microsoft C/C++ and Microsoft Visual C++ supported the long double, 80-bit precision data type. In Win32 programming, however, the long double data type maps to the double, 64-bit precision data type. The Microsoft run-time library provides long double versions of the math functions only for backward compatibility. The long double function prototypes are identical to the prototypes for their double counterparts, except that the long double data type replaces the double data type. The long double versions of these functions should not be used in new code.

0 Kudos
Reply