Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Windows equivalent of nanosleep?

Tags:

winapi

Unix has a variety of sleep APIs (sleep, usleep, nanosleep). The only Win32 function I know of for sleep is Sleep(), which is in units of milliseconds.

I seen that most sleeps, even on Unix, get rounded up significantly (ie: typically to about 10ms). I've seen that on Solaris, if you run as root, you can get sub 10ms sleeps, and I know this is also possible on HPUX provided the fine grain timers kernel parameter is enabled. Are finer granularity timers available on Windows and if so, what are the APIs?

like image 487
Peeter Joot Avatar asked Oct 19 '11 19:10

Peeter Joot


3 Answers

The MinGW answer in long form:

MinGW and Cygwin provides a nanosleep() implementation under <pthread.h>. Source code:

  • In Cygwin and MSYS2: signal.cc and cygwait.cc (LGPLv3+; with linking exception)
    • This is based on NtCreateTimer and WaitForMultipleObjects.
  • In MinGW-W64: nanosleep.c and thread.c (Zope Public License)
    • This is based on WaitForSingleObject and Sleep.

In addition, gnulib (GPLv3+) has a higher-precision implementation in nanosleep.c. This performs a busy-loop over QueryPerformanceCounter for short (<1s) intervals and Sleep for longer intervals.

You can use the usual timeBeginPeriod trick ethanpil linked to with all the underlying NT timers.

like image 196
Mingye Wang Avatar answered Nov 16 '22 08:11

Mingye Wang


If you merely want resolution in the nanoseconds range, there's NtDelayExecution in ntdll.dll:

NTSYSAPI NTSTATUS NTAPI NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval);

It measures time in 100-nanosecond intervals.

HOWEVER, this probably isn't what you want:

It can delay for much longer than that—as long as a thread time slice (0.5 - 15ms) or two.
Here's code you can use to observe this:

#ifdef __cplusplus
extern "C" {
#endif
#ifdef  _M_X64
typedef long long intptr_t;
#else
typedef int intptr_t;
#endif
int __cdecl printf(char const *, ...);
int __cdecl _unloaddll(intptr_t);
intptr_t __cdecl _loaddll(char *);
int (__cdecl * __cdecl _getdllprocaddr(intptr_t, char *, intptr_t))(void);
typedef union _LARGE_INTEGER *PLARGE_INTEGER;
typedef long NTSTATUS;
typedef NTSTATUS __stdcall NtDelayExecution_t(unsigned char Alertable, PLARGE_INTEGER Interval); NtDelayExecution_t *NtDelayExecution = 0;
typedef NTSTATUS __stdcall NtQueryPerformanceCounter_t(PLARGE_INTEGER PerformanceCounter, PLARGE_INTEGER PerformanceFrequency); NtQueryPerformanceCounter_t *NtQueryPerformanceCounter = 0;
#ifdef __cplusplus
}
#endif

int main(int argc, char *argv[]) {
    long long delay = 1 * -(1000 / 100) /* relative 100-ns intervals */, counts_per_sec = 0;
    long long counters[2];
    intptr_t ntdll = _loaddll("ntdll.dll");
    NtDelayExecution = (NtDelayExecution_t *)_getdllprocaddr(ntdll, "NtDelayExecution", -1);
    NtQueryPerformanceCounter = (NtQueryPerformanceCounter_t *)_getdllprocaddr(ntdll, "NtQueryPerformanceCounter", -1);
    for (int i = 0; i < 10; i++) {
        NtQueryPerformanceCounter((PLARGE_INTEGER)&counters[0], (PLARGE_INTEGER)&counts_per_sec);
        NtDelayExecution(0, (PLARGE_INTEGER)&delay);
        NtQueryPerformanceCounter((PLARGE_INTEGER)&counters[1], (PLARGE_INTEGER)&counts_per_sec);
        printf("Slept for %lld microseconds\n", (counters[1] - counters[0]) * 1000000 / counts_per_sec);
    }
    return 0;
}

My output:

Slept for 9455 microseconds
Slept for 15538 microseconds
Slept for 15401 microseconds
Slept for 15708 microseconds
Slept for 15510 microseconds
Slept for 15520 microseconds
Slept for 1248 microseconds
Slept for 996 microseconds
Slept for 984 microseconds
Slept for 1010 microseconds
like image 34
user541686 Avatar answered Nov 16 '22 08:11

user541686


The sad truth is that there is no good answer to this. Multimedia timers are probably the closest you can get -- they only let you set periods down to 1 ms, but (thanks to timeBeginPeriod) they do actually provide precision around 1 ms, where most of the others do only about 10-15 ms as a rule.

There are a lot of other candidates. At first glance, CreateWaitableTimer and SetWaitableTimer probably seem like the closest equivalent since they're set in 100 ns interals. Unfortunately, you can't really depend on anywhere close to that good of resolution, at least in my testing. In the long term, they probably do provide the best possibility, since they at least let you specify a time of less than 1 ms, even though you can't currently depend on the implementation to provide (anywhere close to) that resolution.

NtDelayExecution seems to be roughly the same, as SetWaitableTimer except that it's undocumented. Unless you're set on using/testing undocumented functions, it seems to me that CreateWaitableTimer/SetWaitableTimer is a better choice just on the basis of being documented.

If you're using thread pools, you could try using CreateThreadPoolTimer and SetThreadPoolTimer instead. I haven't tested them enough to have any certainty about the resolution they really provide, but I'm not particularly optimistic.

Timer queues (CreateTimerQueue, CreateTimerQueueTimer, etc.) are what MS recommends as the replacement for multimedia timers, but (at least in my testing) they don't really provide much better resolution than Sleep.

like image 10
Jerry Coffin Avatar answered Nov 16 '22 07:11

Jerry Coffin