Announcement

Collapse
1 of 2 < >

New Sub-Forum

In an effort to help make sure there are appropriate categories for topics of discussion that are happening, there is now a sub-forum for databases and database programming under Special Interest groups. Please direct questions, etc., about this topic to that sub-forum moving forward. Thank you.
2 of 2 < >

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

NtDelayExecution - Sleep/SleepEx() with higher resolution & Absolute delay

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • PBWin/PBCC NtDelayExecution - Sleep/SleepEx() with higher resolution & Absolute delay

    :secret: When you call kernel32!Sleep() / SleepEx(), you're actually just indirectly calling ntdll!NtDelayExecution() but in a watered-down way ...

    ---

    Calling Sleep() / SleepEx() has the advantage of simplicity, but the disadvantages of 1) not offering one of the two timing options NtDelayExecution() provides (only relative not absolute), and 2) only offering millisecond-resolution, as opposed to 100-nanosecond-resolution provided by NtDelayExecution().
    Note: For clarity, while this resolution is expressed to NtDelayExecution in units of 100 nanoseconds, the actual minimum resolution your system is capable of depends on your system clock.

    Kernel32!Sleep() is just a very minimal wrapper for Kernel32!SleepEx() ...
    If you call Sleep(2500) then SleepEx(2500, %FALSE) is called, where %FALSE is the Alertable flag:
    Code:
    /$  8BFF           mov edi,edi                ; kernel32.Sleep(Time)
    |.  55             push ebp
    |.  8BEC           mov ebp,esp
    |.  6A 00          push 0                     ; /Alertable = FALSE
    |.  FF75 08        push dword ptr [arg.1]     ; |Time => [ARG.1]
    |.  E8 4BFFFFFF    call kernel32.SleepEx      ; \KERNEL32.SleepEx
    |.  5D             pop ebp
    \.  C2 0400        retn 4
    SleepEx() itself however is also a fairly simple wrapper, which goes on to call the system function ntdll!NtDelayExecution().

    Not to be outdone, NtDelayExecution() itself is also a wrapper - for KeGetPreviousMode() & KeDelayExecutionThread().

    Also not to be outdone!... KeDelayExecutionThread() itself is a wrapper around various internal KiXxxxx kernel functions such as KiLockDispatcherDatabase, KiInsertTreeTimer, KiSetPriorityThread, KiFindReadyThread, KiReadyThread, KiInsertWaitList, KiComputeWaitInterval and so on ... yes, things start to get a little funky at this level, actually it gets even lower, but we don't need to worry about any of that.

    ---

    Unlike Sleep()/SleepEx() which take an input in milliseconds, NtDelayExecution() takes an input in units of 100 nanoseconds.
    1 millisecond = 1 million (1,000,000) nanoseconds.
    1 nanosecond = 0.000001 milliseconds.
    1 nanosecond = 0.000000001 seconds, ie. 1 billionth of 1 second (1 / 1,000,000,000).


    How is this useful? Well, probably not many will have a need for it (afterall, Microsoft "dumbied down" the resolution from nanoseconds to milliseconds via Sleep()/SleepEx() rather than leaving it as-is). Perhaps this is my least useful Source Code subforum contribution of all time?... then again, perhaps some day somebody will require a need for this level of precision? ... but anyway, I just find it interesting! ... and it never hurts to know how something you work with works (and we've all called Sleep() before!)

    ---

    USAGE:

    NtDelayExecution() takes a 64-bit integer (QUAD) as the time-to-sleep, vs 32-bit (DWORD) for Sleep()/SleepEx().

    If this is a NEGATIVE integer it specifies the relative time to sleep (in units of 100 nanoseconds), otherwise it specifies the absolute time to wake up (the latter being a feature of it which neither Sleep()/SleepEx() cater for -- we have to call NtDelayExecution directly to use that feature).

    Example, using relative time, for the equivalent to Sleep(2500), ie. sleep for 2.5 seconds, we specify -25000000 as the interval with NtDelayExecution(). Using absolute time (which is exclusive to NtDelayExecution and cannot be used via Sleep/SleepEx) we simply get the current system time (UTC, so we use GetSystemTime not GetLocalTime), convert that to filetime, and add 25000000 to the dwLowDateTime field.


    Code:
    '// Sleep for 2.5s / 2500ms / 250000ns
     
    #COMPILE EXE
    #INCLUDE "win32api.inc"
    
    DECLARE FUNCTION NtDelayExecution LIB "ntdll.dll" ALIAS "NtDelayExecution" (BYVAL dwAlertable AS DWORD, BYVAL qInterval AS QUAD PTR) AS LONG
    
    FUNCTION PBMAIN () AS LONG
    LOCAL lDelay AS LONG
    
    ? "Started RELATIVE delay ..."  '// The method used by Sleep/SleepEx
    LOCAL qInt AS QUAD
    qInt = -25000000
    lDelay = NtDelayExecution (%FALSE, BYVAL VARPTR(qInt))
    ? "Finished."
    
    
    ? "Started ABSOLUTE delay ..."  '// Unique to NtDelayExecution (not available via Sleep/SleepEx)
    LOCAL ft AS FILETIME
    GetSystemTimeAsFileTime ft
    ft.dwLowDateTime = ft.dwLowDateTime + 25000000
    lDelay = NtDelayExecution (%FALSE, BYVAL VARPTR(ft))
    ? "Finished."
    
    
    #IF %DEF(%PB_CC32)
     STDOUT "Done, press any key to continue...";: WAITKEY$
    #ENDIF
    END FUNCTION
    ps. Just in case you're wondering, no - Sleep/SleepEx/DelayExecution do not hog the CPU at all ... exactly the opposite actually - they free the CPU to process other threads/processes (you should find your process sitting on 0% CPU usage in Task Manager during Sleep delays, assuming no other threads in your process are active obviously).

    Note that Sleep/SleepEx/NtDelayExecution only sleeps/suspends the current thread ie. calling thread. All other threads in the calling process remain in their current state.

    For examples of this see David Roberts UltraSleep and MaximizeTimerResolution which are available in both DLL & SLL forms you can directly use.
    Last edited by Wayne Diamond; 20 Aug 2014, 10:39 AM.
    -

  • #2
    takes an input in units of 100 nanoseconds.
    However, it does not have a resolution of 100 nanoseconds. It uses the system clock with a frequency of 64Hz; as does Sleep.

    Try this:

    Code:
    #Compile Exe
    #Include "Win32API.inc"
     
    Declare Function ZwDelayExecution Lib "ntdll.dll" Alias "ZwDelayExecution" (ByVal dwAlertable As Dword, qInterval As Quad) As Long
    
    Macro SleepX(n) = ZwDelayExecution (%FALSE, -10000*n) ' => ms
     
    Function PBMain () As Long
    Local i As Long
    Local qQPCFreq, qStart, qStop, qQPCOverhead As Quad
    Local msg As String
    
    QueryPerformanceFrequency qQPCFreq
    QueryPerformanceCounter qStart ' Ignore first query
    QueryPerformanceCounter qStart
    QueryPerformanceCounter qStop
    qQPCOverhead = qStop - qStart
    
    For i = 1 To 10
      QueryPerformanceCounter qStart
      SleepX(5)
      QueryPerformanceCounter qStop
      msg += Str$((qStop - qStart - qQPCOverhead)*1000/qQPCFreq) + $CrLf
      Sleep 15 ' Take a breather
    Next
    
    MsgBox msg
    
    End Function
    We should always ignore the first query of any timer.

    It should be noted that if you run the above with the Google Chrome browser open then you will get millisecond accuracy. Chrome uses TimeBeginPeriod which is a global setting causing a shift from the system clock to a multimedia timer. Needless to say Chrome uses TimeEndPeriod before closing. This is also true of the Comodo Dragon browser but not so with Internet Explorer and, probably, others.

    Comment


    • #3
      it would help if ZwDelayExecution was properly documented, yes

      related... (very unrefined code though)
      Code:
      #COMPILE EXE
      #INCLUDE "win32api.inc"
      
      FUNCTION SysUptime() AS LONG
      LOCAL secTiming AS QUAD, secFreq AS QUAD
      IF QueryPerformanceCounter (secTiming) THEN
         QueryPerformanceFrequency secFreq
         FUNCTION = secTiming \ secFreq 'how long windows has been running in seconds.
      END IF
      END FUNCTION
      
      
      FUNCTION PBMAIN() AS LONG
          STDOUT STR$(SysUptime)
          SLEEP 1000
          STDOUT STR$(SysUptime)
          SLEEP 1000
          STDOUT STR$(SysUptime)
          SLEEP 1000
      'STDOUT STR$(lTime)
      'SLEEP 3000
      'QueryPerformanceCounter (secTiming)
      'IF QueryPerformanceCounter (secTiming) THEN
      '   STDOUT STR$(secTiming)
      '   QueryPerformanceFrequency secFreq
      ' '  lTime = secTiming \ secFreq 'how long windows has been running in seconds.
      'END IF
      'STDOUT STR$(lTime)
      WAITKEY$
      END FUNCTION
      Last edited by Wayne Diamond; 12 Aug 2014, 10:49 AM.
      -

      Comment


      • #4
        Confirmation that it is indeed in 100 nanosecond units, from the book "Windows NT/2000 Native API Reference" by Gary Nebbett

        http://books.google.com.au/books?id=...ed=0CB0Q6AEwAA

        Page 243...

        ZwSetTimerResolution

        ZwSetTimerResolution sets the resolution of the system timer.

        NTSYSAPI
        NTSTATUS
        NTAPI
        ZwSetTimerResolution(
        IN ULONG RequestedResolution,
        IN BOOLEAN Set,
        OUT PULONG ActualResolution
        );

        Parameters

        RequestedResolution
        The requested timer resolution in units of 100-nanoseconds.

        Set
        Specifies whether the requested resolution should be established or revoked.

        ActualResolution
        Points to a variable that recieves the actual timer resolution in units of 100-nanoseconds.

        Return Value
        Returns STATUS_SUCCESS or an error status, such as STATUS_TIMER_RESOLUTION_NOT_SET.

        ---
        PAGE 244 IS MISSING FROM THE PREVIEW
        Unfortunately, it is page 244 which contains ZwDelayExecution, but page 245 has the last part of it...
        ---
        Page 245...

        Interval
        Points to a value that specifies the absolute or relative time at which the delay is to end. A negative value specifies an interval relative to the current time. The value is expressed in units of 100 nanoseconds. Absolute times track any changes in the system time; relative times are not affected by system time changes.


        Return Value
        Returns STATUS_SUCCESS, STATUS_ALERTED, STATUS_USER_APC, or an error status.

        Related Win32 Functions
        Sleep, SleepEx.

        Remarks
        SleepEx exposes most of the functionality of ZwDelayExecution.
        -

        Comment


        • #5
          Discussion here.

          Comment

          Working...
          X