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

trouble with "sleep"

Robert_van_Amerongen
New Contributor III
1,401 Views

When I add the window api subroutine "Sleep" to my collection, it did not work as intended. It turned out that there is another subroutine "Sleep". The following program does run fine:

 

  PROGRAM sleep_demo
!  USE IFPORT                       !  hè?? not needed
  IMPLICIT none
!
  INTEGER :: time1, time2, rate
!
  CALL SYSTEM_CLOCK(COUNT=time1, COUNT_RATE=rate)
  CALL sleep(10)                                    ! suspend for 10 seconds
  CALL SYSTEM_CLOCK(COUNT=time2)
  WRITE(*,*) 'elapsed time:', REAL(time2-time1)/REAL(rate)
!
  STOP
  END PROGRAM sleep_demo

Surprisingly, there is no need to use IFPORT (or any other related module.) although its addition does not harm. I expect compiling and linking this program according to the standard to produce an unresolved reference to "Sleep". But that is not the case. It works fine. (The winapi routine "Sleep" takes as an argument the number of milliseconds to wait.)

It this correct?

0 Kudos
9 Replies
jimdempseyatthecove
Honored Contributor III
1,401 Views

SLEEP takes INTEGER(4) duration in seconds
SLEEPQQ takes INTEGER(4) duration in milliseconds

Both supplied routines require IFPORT.

Due to "CALL sleep(10)" SLEEP or _SLEEP or other decorated name will be produced depending on platform and options. For it to link with Sleep is unexpected. What is your platform and options (both compiler and linker), as well as any additional objs and libraries you link to the program?

Jim Dempsey

0 Kudos
IanH
Honored Contributor II
1,401 Views

The ifort provided SLEEP is a Fortran 77 style external procedure.  All the IFPORT module does is provide an explicit interface for that procedure, that in this case lets the compiler check that the programmer isn't doing something silly (in other cases it would also enable the procedure to use Fortran 90 style argument declarations, such as deferred shape/allocatable/optional, but SLEEP doesn't have anything fancy like that).  You can find the source for the IFPORT module with the interface in your compiler installation directory tree.

By being a Fortran 77 style external procedure, SLEEP can be called from Fortran 77 code - no USE statements, no interface blocks, etc.

The compiler emits directives into the object code that it generates, that instructs the linker that it should link against a set of default libraries that are pretty much required for any Fortran program.  libifport, which contain the ifort provided SLEEP procedure, is one of those default libraries, hence the linker is able to find the definition of the external procedure.

Without a use statement for IFPORT (or otherwise providing an interface block for SLEEP), the sleep procedure is being referenced via an identifier that does not have the EXTERNAL attribute - there is nothing in the supplied Fortran source that tells the compiler that SLEEP is a procedure.  Fortran 2015 provides an enhancement to the IMPLICIT statement that indicates that you want a diagnostic from the compiler when you reference a procedure via an identifier in this manner - you would either add an `IMPLICIT NONE (EXTERNAL)` statement to your source, or change the existing implicit statement to `IMPLICIT NONE (TYPE, EXTERNAL)`.  But that's for the years ahead.

(If an intel/ex-intel J3 person is listening, the F2015 implicit none statement needs a constraint added to stop repetitions of a particular implicit-none-spec.)

0 Kudos
Robert_van_Amerongen
New Contributor III
1,401 Views

Thanks both of you. It makes thing clear. I have added the buildlog file for any inspection.

The question remains: I have not asked for a link to sleep. But Intel is so kind to give it to me without any asking from my side. The next standard seems to give the opportunity to let issue a warning. In the mean time: is there a MSVS option with which I can let the compilation / link fail (as a standard comforming program ought to do)?

Best regards,

Robert

 

0 Kudos
Robert_van_Amerongen
New Contributor III
1,401 Views

Builslog seems disapeared.

New try.

0 Kudos
IanH
Honored Contributor II
1,401 Views

Robert van Amerongen wrote:

The question remains: I have not asked for a link to sleep. But Intel is so kind to give it to me without any asking from my side. The next standard seems to give the opportunity to let issue a warning. In the mean time: is there a MSVS option with which I can let the compilation / link fail (as a standard comforming program ought to do)?

If the program is standard conforming, then the compiler should accept it without too much complaint (otherwise the compiler's vendor will get complaints!).

If the program isn't standard conforming, then the compiler may complain, depending on the nature of the non-conformance.

In this case, the compiler doesn't know whether the program is non-conforming or not.  Did the programmer intend to call the Intel provided SLEEP (or some other external procedure called SLEEP, that has been separately compiled) or not?  The rules of the language permit implicit references to external procedures via identifiers that don't have the external attribute - something which a large majority of Fortran 77 source codes did.

You can add /link /nodefaultlib:libifport.lib to the command line and the linker will then ignore the the compiler provided directive to link against the library that provides SLEEP.  In the absence of some other definition, you will then get an unresolved symbol error at link time.  The equivalent property within a Visual Studio project is to put libifport.lib in the Linker > Input, Ignore Specific Library property.  If you then, later, require different routines from that library, you will also get a link error, but you can always remove the nodefaultlib option, or specify the library explicitly as linker input.

(The name of libifport.lib may have changed with ifort version, but that's what it has been called for the last few releases at least.)

0 Kudos
Paul_Curtis
Valued Contributor I
1,401 Views

Here is a subroutine to insert a time-delay into an IVF program on Windows.  This is probably more WinAPI and less "standard" Fortran than the OP is looking for, but it works perfectly, and blocks only the calling thread which is important for multithreaded programs where the intent is not to hang the entire system.

RECURSIVE SUBROUTINE delay_ms (howmany)
	USE kernel32, ONLY: WaitForSingleObject, CloseHandle, CreateEvent !sleep
	IMPLICIT NONE
	INTEGER, INTENT(IN)	:: howmany
	INTEGER(HANDLE)     :: hEvent
	INTEGER             :: rval

	!	Win32 suspend function, allows the timeslice to be
	!	used by other threads until the timeout elapses
	IF (howmany <= 0) RETURN
	
	!   original method, seems to block all threads
	!   not just the calling thread
	!IF (howmany > 0) CALL sleep (howmany)

    !   new method, blocks only the calling thread
	hEvent = CreateEvent       (NULL_SECURITY_ATTRIBUTES,	& 
	    	   	                TRUE,					    & ! manual reset
		    			        FALSE,  			        & ! initial status
						        NULL)  					      ! unnamed	event object
    rval = WaitForSingleObject (hEvent, howmany)
    rval = CloseHandle         (hEvent)

END SUBROUTINE delay_ms


 

0 Kudos
Robert_van_Amerongen
New Contributor III
1,401 Views

Again, thank you both.

I have tried to set the linker option with ignoring libifport.lib, but that does not give the required response. The file still exists with that name.

There is another option: use the Winapi function SleepEx. No interference with whatever Sleep function.

Paul's suggesting is very nice: I did not use it in a Multi threading environment and thus never realised the Sleep wil halt all threads! 

Robert

0 Kudos
IanH
Honored Contributor II
1,401 Views

Sleep halting all threads in the process would surprise me - one of the uses of Sleep is to relinquish the time slice of the current thread so that other threads (in the same process or in other processes) can run. 

Some simple testing here demonstrates that other threads in the process quite happily continue to execute while their colleagues are snoozing.


 

! Program that does a lot of sleeping, on different threads.
!
! Each thread loops, sleeping for some time then writing some 
! output (a sequence of characters) directly to a character 
! cell on the console.

MODULE RatherSleepyMod
  USE IFWIN
  IMPLICIT NONE
  
  PRIVATE
  
  PUBLIC :: Run
  
  ! Nuber of threads, including main thread.
  INTEGER, PARAMETER :: num_threads = 24
  
  ! Total time (approx) to run in ms.
  INTEGER, PARAMETER :: total_time = 10000
  
  ! Attributes to draw output of the threads, cycled through by thread index.
  INTEGER(WORD), PARAMETER :: attributes(*) = [  &
      IANY([FOREGROUND_BLUE, FOREGROUND_INTENSITY]),  &
      IANY([FOREGROUND_GREEN, FOREGROUND_INTENSITY]),  &
      IANY([FOREGROUND_RED, FOREGROUND_INTENSITY]),  &
      IANY([FOREGROUND_BLUE, FOREGROUND_GREEN, FOREGROUND_INTENSITY]),  &
      IANY([FOREGROUND_BLUE, FOREGROUND_RED, FOREGROUND_INTENSITY]),  &
      IANY([FOREGROUND_GREEN, FOREGROUND_RED, FOREGROUND_INTENSITY]),  &
      IANY([FOREGROUND_BLUE, FOREGROUND_GREEN, FOREGROUND_RED, FOREGROUND_INTENSITY]) ]
  
  ! Number of cycles for the threads, cycled through by thread index.
  INTEGER, PARAMETER :: counts(*) = [  &
      1, 10, 50, 100, 200, 500, 1000, 2000, 5000, 10000 ]
  
  ! Handle to the console output buffer.
  INTEGER(HANDLE) :: conout_handle
  
  ! Original console buffer info.
  TYPE(T_CONSOLE_SCREEN_BUFFER_INFO) :: original_buffer_info
CONTAINS
  SUBROUTINE Run
    INTEGER(DWORD) :: thread_id
    INTEGER(DWORD) :: dwrc
    INTEGER(BOOL) :: brc
    INTEGER(HANDLE) :: thread_handles(2:num_threads)
    INTEGER :: i
    
    ! Get a handle to the console output buffer.
    conout_handle = GetStdHandle(STD_OUTPUT_HANDLE)
    
    ! Get the current cursor position so that we put the output somewhere 
    ! that is likely visible to the user.
    brc = GetConsoleScreenBufferInfo(conout_handle, original_buffer_info)
    IF (brc == 0) THEN
      ERROR STOP 'stdout not attached to a console or other silliness'
    END IF
    IF (original_buffer_info%dwSize%Y < num_threads) THEN
      ERROR STOP 'Too many threads for console size'
    END IF
    
    ! Start the other threads.
    DO i = 1, num_threads
      IF (i /= 1) THEN
        thread_handles(i) = CreateThread(  &
            NULL,  &
            0_DWORD,  &
            LOC(thread_proc),  &
            INT(i, LPVOID),  &
            0_DWORD,  &
            LOC(thread_id) )
      END IF
    END DO
    
    ! Do some work on the main thread too.
    CALL thread_proc(INT(1, LPVOID))
    
    ! Wait for all the others to finish.
    dwrc = WaitForMultipleObjects(  &
        SIZE(thread_handles, KIND=DWORD),  &
        LOC(thread_handles),  &
        TRUE,  &
        INFINITE )
     
    ! Clean up.
    DO i = 2, SIZE(thread_handles)
      brc = CloseHandle(thread_handles(i))
    END DO
  END SUBROUTINE Run
  
  
  RECURSIVE SUBROUTINE thread_proc(idx) BIND(C)
    INTEGER(LPVOID), VALUE :: idx
    !DEC$ ATTRIBUTES STDCALL :: thread_proc
    
    INTEGER :: i
    CHARACTER :: c
    INTEGER :: my_count
    
    my_count = counts(MOD(idx - 1, SIZE(counts)) + 1)
    
    c = '0'
    CALL output_char(idx, c)
    DO i = 1, my_count
      CALL Sleep(INT(total_time / my_count, DWORD))
      c = ACHAR(IACHAR('0') + MOD(i, 32))
      CALL output_char(idx, c)
    END DO
  END SUBROUTINE thread_proc
  
  
  RECURSIVE SUBROUTINE output_char(idx, c)
    INTEGER(LPVOID), INTENT(IN) :: idx
    CHARACTER, INTENT(IN) :: c
    
    INTEGER(BOOL) :: brc
    TYPE(T_CHAR_INFO) :: buffer(1,1)
    TYPE(T_SMALL_RECT) :: write_region
    
    INTERFACE
      FUNCTION WriteConsoleOutput_but_better( hConsoleOutput, lpBuffer, &
            dwBufferSize, dwBufferCoord, lpWriteRegion )  &
          BIND(C, NAME='WriteConsoleOutputA')
        USE IFWINTY, ONLY: BOOL, HANDLE, T_CHAR_INFO, T_COORD, T_SMALL_RECT
        IMPLICIT NONE
        INTEGER(HANDLE), VALUE :: hConsoleOutput
        TYPE(T_CHAR_INFO), INTENT(IN) :: lpBuffer(*)
        TYPE(T_COORD), VALUE :: dwBufferSize
        TYPE(T_COORD), VALUE :: dwBufferCoord
        TYPE(T_SMALL_RECT), INTENT(INOUT) :: lpWriteRegion
        INTEGER(BOOL) :: WriteConsoleOutput_but_better
        !DEC$ ATTRIBUTES STDCALL :: WriteConsoleOutput_but_better
      END FUNCTION WriteConsoleOutput_but_better
    END INTERFACE
    
    buffer(1,:)%AsciiChar = c
    buffer(1,:)%Attributes = attributes(MOD(idx - 1, SIZE(attributes))+1)
    write_region%Left = idx - 1
    write_region%Top = original_buffer_info%dwCursorPosition%Y
    write_region%Right = idx - 1
    write_region%Bottom = original_buffer_info%dwCursorPosition%Y
    brc = WriteConsoleOutput_but_better(  &
        conout_handle,  &
        buffer,  &
        T_COORD(SIZE(buffer, 1), SIZE(buffer, 2)),  &
        T_COORD(0, 0),  &
        write_region )
  END SUBROUTINE output_char
END MODULE RatherSleepyMod


PROGRAM RatherSleepy
  USE RatherSleepyMod
  IMPLICIT NONE
  CALL Run
END PROGRAM RatherSleepy

 

0 Kudos
Paul_Curtis
Valued Contributor I
1,401 Views

@IanH -- I had initially used Sleep() in a program which is agressively multi-threaded with many simultaneous streams of serial communications.  Sleep() seemed to work as advertised, until I added more threads of Winsock communications, whereupon Sleep() serialized the entire program.  Changing from Sleep() to the event-based method made this problem go away.  So a simple example where Sleep() seems to work proves only that simple examples can be simplistic, and there are far more subtle tests waiting to trip you up deep within Windows.

We (well, this programmer, at least) have neither knowledge nor control of how Sleep() is actually implemented, but we do understand what's going on by creating a non-signalled event which will never be signalled because it is not hooked up to anything, and hence WaitForSingleEvent() will return exactly as expected after its specified timeout.  This appears to be thread-safe in all (mystery) contexts on the Windows side of the fence, as it better be because the WaitFor*Events() method is ubiquitous throughout Windows.

0 Kudos
Reply