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

Interface for winnt.h InterlockedAdd and linking

MWind2
New Contributor III
910 Views

So far I have gone into a rabbit hole. InterlockedAdd is an intrinsic to MS, not in a lib, so I guess I have to make a dll in MSVC that invokes InterlockedAdd and returns result.  

 
0 Kudos
15 Replies
jimdempseyatthecove
Honored Contributor III
910 Views

Kernel32.f90 has:

INTERFACE 
FUNCTION InterlockedExchangeAdd( &
        Addend, &
        Value)
import
  integer(LONG) :: InterlockedExchangeAdd ! LONG
    !DEC$ ATTRIBUTES DEFAULT, DECORATE, ALIAS:'__ifort_InterlockedExchangeAdd' :: InterlockedExchangeAdd
  integer(LONG), intent(INOUT) :: Addend ! LPLONG Addend
    !DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC :: Addend
  integer(LONG), intent(IN) ::  Value ! LONG Value
    !DEC$ ATTRIBUTES VALUE :: Value
 END FUNCTION
END INTERFACE

This returns the old value prior to interlocked add

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
910 Views

For completeness, here are the set of interlocked operations defined in KERNEL32:

InterlockedIncrement, InterlockedDecrement, InterlockedExchange, InterlockedExchangeAdd, InterlockedCompareExchange

For coarray programs, there are a bunch of ATOMIC_xxx intrinsics, such as ATOMIC_ADD.

0 Kudos
jimdempseyatthecove
Honored Contributor III
910 Views

Also, you can use OpenMP ATOMIC
 

!$omp atomic capture
oldValue = x
x = x + expression

! .OR.

!$omp atomic capture
x = x + expression
newValue = x

The first returns prior value, the second returns resultant value.

Jim Dempsey

0 Kudos
MWind2
New Contributor III
910 Views

@jimdempseyatthecove:

Thanks, I did not know about source like kernel32.f90.  I was dumbin'ing kernel32.lib and getting nowhere.  Using the code gets

"error #6623: The procedure name of the INTERFACE block conflicts with a name in the encompassing scoping unit.   [INTERLOCKEDEXCHANGEADD]".

 

0 Kudos
MWind2
New Contributor III
910 Views

This compiles except for the error#6623 above, but I would have thought I should have a CPI1 C_PTR declared and used:

INTEGER :: I0 =1
  INTEGER(C_INT), VOLATILE, TARGET :: I1 = 2
  INTEGER :: I2= 8
  I0 = INTERLOCKEDEXCHANGEADD(I1,I2)

 

0 Kudos
FortranFan
Honored Contributor II
910 Views

mwindham wrote:

This compiles except for the error#6623 above, but I would have thought I should have a CPI1 C_PTR declared and used:

INTEGER :: I0 =1
  INTEGER(C_INT), VOLATILE, TARGET :: I1 = 2
  INTEGER :: I2= 8
  I0 = INTERLOCKEDEXCHANGEADD(I1,I2)

 

You can try this, note the IA32 (effectively 32-bit) environment.  On x64 (64-bit), use InterLockedExchangeAdd64.

   use, intrinsic :: iso_c_binding, only : c_long

   interface
      function InterlockedExchangeAdd(Addend,Value) result(r) bind(C, name="InterlockedExchangeAdd")
      !DIR$ ATTRIBUTES STDCALL :: InterlockedExchangeAdd

      ! MSDN function prototype:
      ! https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockedexchangeadd
      ! LONG InterlockedExchangeAdd(
      !   LONG volatile *Addend,
      !   LONG          Value
      !);
         import :: c_long
         implicit none
         ! Argument list
         integer(c_long), volatile, intent(inout) :: Addend
         integer(c_long), value, intent(in)       :: Value
         ! Function result
         integer(c_long) :: r
      end function
   end interface

   ! Local variables
   integer(c_long) :: I0 = 1
   integer(c_long), volatile :: I1 = 2
   integer(c_long) :: I2 = 8

   I0 = InterlockedExchangeAdd(I1, I2)

   print *, "I0 = ", I0

   stop

end

Upon compilation, linking, and execution:

C:\Temp>ifort /standard-semantics /warn:all /stand:f18 p.f90
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on IA-32, Version 19.1.0.085 Pre-Release Beta Build 20190522
Copyright (C) 1985-2019 Intel Corporation.  All rights reserved.

p.f90(5): warning #7025: This directive is not standard F2018.
      !DIR$ ATTRIBUTES STDCALL :: InterlockedExchangeAdd
------------^
Microsoft (R) Incremental Linker Version 14.21.27702.2
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:p.exe
-subsystem:console
p.obj

C:\Temp>p.exe
 I0 =  2

C:\Temp>

 

0 Kudos
MWind2
New Contributor III
910 Views
I think the error#6623 above was a clash with something already in kernel32.f90. This works ith LOC(i1) but does not compile with the C_LOC(I1) arg. In debugger i1 is 10.

program Interlocked1
USE KERNEL32
use iso_c_binding
    implicit none

    ! Variables
  INTEGER :: I0 =1
  INTEGER(C_INT), VOLATILE, TARGET :: I1 = 2
  INTEGER :: I2= 8
  
    ! Body of Interlocked
    
    I0 = INTERLOCKEDEXCHANGEADD(LOC(I1),I2)
    !I0 = INTERLOCKEDEXCHANGEADD(C_LOC(I1),I2)
    print *, 'Hello World'

    end program Interlocked1

 

0 Kudos
MWind2
New Contributor III
910 Views

Thanks to all.

0 Kudos
FortranFan
Honored Contributor II
910 Views

mwindham wrote:

I think the error#6623 above was a clash with something already in kernel32.f90. This works ith LOC(i1) but does not compile with the C_LOC(I1) arg. In debugger i1 is 10. ..

Refer to the documentation for the non-standard LOC function, an Intel Fortran extension, and the standard-supported intrinsic function of C_LOC from ISO_C_BINDING intrinsic module (https://software.intel.com/en-us/fortran-compiler-developer-guide-and-reference-c-loc) whose return type in a Fortran-standard defined derived type of C_PTR and not what the interface for InterLockedExchangeAdd expects,

Separately, the interface declaration is already to setup to do the 'work' in terms of 'pass by reference' of the first parameter in that function, so you don't need to use LOC nor C_LOC with it.

0 Kudos
MWind2
New Contributor III
910 Views

@FortranFan

if LOC(I1)  was omitted, I got exception for trying to access address 0x00000002.

0 Kudos
FortranFan
Honored Contributor II
910 Views

mwindham wrote:

@FortranFan

if LOC(I1)  was omitted, I got exception for trying to access address 0x00000002.

It's straight forward with using the Windows OS included C++ function in kernel32.dll from Microsoft as shown above in Quote #7.

I'm unsure of the interface with alias '__ifort_InterlockedExchangeAdd' in Intel Fortran provided kernel32.f90 file.

0 Kudos
MWind2
New Contributor III
910 Views

@FortranFan

Your codes works, as does mine(both compiled 32-bit). Mine requires LOC and uses kernel32.f90 interface; yours is explicit.

So does this, without TARGET:

program Interlocked1a

    USE KERNEL32
    use iso_c_binding
    implicit none
    ! Variables
    INTEGER :: I0 =1
    INTEGER, VOLATILE :: I1 = 2
    INTEGER :: I2= 8
    ! Body of Interlocked
    I0 = INTERLOCKEDEXCHANGEADD(LOC(I1),I2)
    print *, "I1 = ", I1
    end program Interlocked1a

 

0 Kudos
MWind2
New Contributor III
910 Views

Quote #2 was right and I got it wrong by trying to redo interface in kernel32.f90 in mine first time around. I am confused about what IGNORE_LOC does? It seems a LOC for Addend is needed.

0 Kudos
FortranFan
Honored Contributor II
910 Views

mwindham wrote:

Quote #2 was right and I got it wrong by trying to redo interface in kernel32.f90 in mine first time around. I am confused about what IGNORE_LOC does? It seems a LOC for Addend is needed.

If you are able to contact Intel Support, you may want to inquire with them re: Kernel32.f90 interface for Fortran.  One would have thought the 'IGNORE_LOC' attribute is present in the interface so that one doesn't need to use LOC, so something appears amiss.

0 Kudos
IanH
Honored Contributor II
910 Views

Jim has shown the x64 declaration of that routine.  On x64, Windows expects this API to be implemented by a compiler intrinsic and consequently it is not exported from kernel32.dll.  ifort's runtime implements the API itself - hence the alias to an ifort runtime helper routine.  The declaration and implementation of that helper routine is such that LOC does not have to be used (default calling convention, the Addend argument is passed by reference, IGNORE_LOC will do what it suggests).

The definition for x86 is different - LOC is required.  On x86 this API is implemented using an export from kernel32.lib, the declaration is consistent with other API's (stdcall calling convention, the argument is passed by value, consequently the value passed needs to be the address of the thing that you want to exchange and add).

But... why are you calling this API? 

 

 

0 Kudos
Reply