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

setjmp/longjmp x64

netphilou31
New Contributor II
3,614 Views

Hi,

I'm working on old code that was originally only built as a 32-bit version. This code uses the setjmp/longjmp routines to easily break out of a location that may be deep inside the call sequence. Originally a simple STOP statement was used, but when the code was turned into a DLL, it was the easiest way to quit the DLL. It works perfectly in a 32-bit setup, but when I try to test this feature with a 64-bit DLL, I couldn't get it to work. Every time I try to call the longjmp routine an exception is thrown somewhere in ntdll.dll. The jmp_buf object seems to match the one defined in the setjmp.h source file, which is different between the 32-bit and 64-bit versions.

Is there something more to do for 64-bit or it just doesn't work in 64-bit?

Regards,

 

P.S.: Here is my source code:

    use IFWIN, only: UINT64

!dec$ if defined(_X86_)
    integer(4), public :: JMP_BUF(16)
!dec$ else
    type SETJMP_FLOAT128
        sequence
        integer(UINT64) Part(2)
    end type SETJMP_FLOAT128
    type(SETJMP_FLOAT128), public :: JMP_BUF(16)
!dec$ endif

!dec$ if defined(_X86_)
    interface
        integer(4) function SETJMP(JMP_BUF)
        !dec$ attributes C, alias:'__setjmp' :: SETJMP
            integer(4) :: JMP_BUF(*)
        end function
    end interface
    interface
        subroutine LONGJMP(JMP_BUF, RET_VALUE)
        !dec$ attributes C :: LONGJMP
            integer(4) :: JMP_BUF(*)
            integer(4) :: RET_VALUE
        end subroutine
    end interface
!dec$ else
    interface
        integer(4) function SETJMP(JMP_BUF)
        !dec$ attributes C :: SETJMP
            import
            type(SETJMP_FLOAT128) :: JMP_BUF(*)
        end function
    end interface
    interface
        subroutine LONGJMP(JMP_BUF, RET_VALUE)
        !dec$ attributes C :: LONGJMP
            import
            type(SETJMP_FLOAT128) :: JMP_BUF(*)
            integer(4)            :: RET_VALUE
        end subroutine
    end interface
!dec$ endif

 

0 Kudos
25 Replies
netphilou31
New Contributor II
525 Views

I am not sure to understand what you mean by:

"You will (should) find that the allocation was not returned"

Did you meant, passing an allocatable array to the subroutine that performs the longjmp call, and allocating the corresponding dummy argument in that routine before the call to longjmp()?

About the files that may be not closed before the call to longjmp(), the routine that handle the longjmp call makes sure to close these files, and if no, the calling process, on return with such an error condition, calls another entry point that performs the desired closes.

Phil.

0 Kudos
jimdempseyatthecove
Honored Contributor III
414 Views

>>Did you meant, passing an allocatable array to the subroutine that performs the longjmp call, and allocating the corresponding dummy argument in that routine before the call to longjmp()?

No, I though I was clear.

DLL "main" sets return for long jump, DLL main calls subroutine FOO, subroutine FOO has local (not dummy) allocatable array, FOO allocates array, FOO issues longjmp before deallocating array, DLL main returns allocation of that array still allocated (iow memory leak). Array will not be deallocated if there isn't a mechanism such as that on normal return.

Note, in stepping through the x64 dissassembly code of the longjmp, it appeared as if there is an IVF shell function construction that was attempting to return through the normal RETURN cleanup code, which seemed to be caught in an endless loop (I quit after stepping in the debugger after several iterations). As to if this code is a broken attempt at (longjmp) exception handling I cannot say for sure, but it suspiciously looks like it is as the non-shell C longjmp will simply restore the SP, PC and pass error code. If you step into the disassembly code at line 23 (longjmp) you can see that a shell function is used and that it iterates in a much more complicated manner (which I suspect is broken code).

 

Jim Dempsey

0 Kudos
netphilou31
New Contributor II
393 Views

Jim,

Thanks for the clarification. I didn't plan any call to longjmp() without deallocating all arrays locally allocated so I was confused with your answer, but I agree that all allocated array not deallocated before the longmp() call will lead to memory leaks. Normally, even if deallocation is automatically done by the compiler for non-SAVED arrays on subroutine exit, I prefer to deallocate them manually before exiting my subroutines/functions.

Phil.

0 Kudos
jimdempseyatthecove
Honored Contributor III
383 Views

>>I didn't plan any call to longjmp() without deallocating all arrays locally allocated so I was confused with your answer,

Consider:

 

function entry
  ...
  setjmp(...)
  call FEE(..)
      subroutine FEE(...)
         ...
         allocate(localArrayA(n))
            call FI(...)
               subroutine FI(...)
                  ...
                  allocate(localArrayB(n))
                  ...
                  if(error) then
                     delete(localArrayB)
                     longjmp(...) ! ukp-level localArrayA still allocated
                  endif
                  ...

 

Unless your code is (and will always be) one level deep from point of longjmp, your local error handling (performing the deletes) will not perform (potential) up-level deletes. What you need then, is what amounts to your own analog (Fortran compliant) "try/catch".

I would suggest starting with IanH's shell functions, modified to permit stacking try-levels. I will leave it an exercise for you to write the code to implement the usage such as:

 

subroutine FOO(...)
...
use trycatch
type(try_t) :: try ! link's to upper-level try's
...
do
  if(try%begin()) then
    ... ! protected code
    allocate(A(n))
    ...
  else
    ... ! cleanup
    delete(A)
    if(catch_code_fatal_expr) call try%throw(catch_code_fatal) ! pop's level
    ... ! retry code
    cycle ! attempt recovery
  endif
  ... ! unprotected code
end do
...
return ! effectively pop's try
end subroutine FOO

 

This should not be all that hard to write (incorporating the exception handling into your code is a different story).

 

Jim Dempsey

 

0 Kudos
netphilou31
New Contributor II
347 Views

Jim,

I fully agree with your comments, and I will try to play a bit around with your code suggestion (and Ian code sample).

Thanks.

Phil.

0 Kudos
Reply