Hi. Here's a patch that uses high res timers for clock_nanosleep.
*** clock.c.orig Sun Jan 2 13:40:55 2022
--- clock.c Sun Jan 2 14:09:09 2022
***************
*** 6,11 ****
--- 6,12 ----
#include <errno.h>
#include <stdint.h>
+ #include <stdbool.h>
#include <time.h>
#include <windows.h>
#ifndef IN_WINPTHREAD
***************
*** 213,221 ****
return lc_set_errno(EINVAL);
}
/**
* Sleep for the specified time.
! * @param clock_id This argument should always be CLOCK_REALTIME (0).
* @param flags 0 for relative sleep interval, others for absolute waking up.
* @param request The desired sleep interval or absolute waking up time.
* @param remain The remain amount of time to sleep.
--- 214,259 ----
return lc_set_errno(EINVAL);
}
+ //
https://randomascii.wordpress.com/2020/10/04/windows-timer-resolution-the-great-rule-change/
+ // Feature available in Windows 2004 and later
+ #define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002
+
+ // Windows 2004,
https://docs.microsoft.com/en-us/windows/release-health/release-information
+ #define Win2004_BUILD_NUMBER 19041
+
+ static inline
+ bool haveHighResTimer()
+ {
+ char bn[32];
+ DWORD sz = sizeof(bn);
+
+ // Determine whether Windows version is at least Win 2004
+ int s = RegGetValueA(HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "CurrentBuildNumber", RRF_RT_REG_SZ, 0, bn, &sz);
+ if (!s) {
+ int curVer = atoi(bn);
+ return (curVer >= Win2004_BUILD_NUMBER);
+ }
+ return false;
+ }
+
+ static inline
+ void timersub(struct timespec* c,
+ const struct timespec* a,
+ const struct timespec* b)
+ {
+ c->tv_sec = a->tv_sec - b->tv_sec;
+ c->tv_nsec = a->tv_nsec - b->tv_nsec;
+ if (c->tv_nsec < 0) {
+ c->tv_sec--;
+ c->tv_nsec += POW10_9;
+ }
+ }
+
/**
* Sleep for the specified time.
! * @param clock_id: CLOCK_REALTIME or CLOCK_MONOTONIC
* @param flags 0 for relative sleep interval, others for absolute waking up.
* @param request The desired sleep interval or absolute waking up time.
* @param remain The remain amount of time to sleep.
***************
*** 225,252 ****
* with errno set to indicate the error.
*/
int clock_nanosleep(clockid_t clock_id, int flags,
! const struct timespec *request,
! struct timespec *remain)
{
struct timespec tp;
! if (clock_id != CLOCK_REALTIME)
! return lc_set_errno(EINVAL);
! if (flags == 0)
! return nanosleep(request, remain);
! /* TIMER_ABSTIME = 1 */
! clock_gettime(CLOCK_REALTIME, &tp);
! tp.tv_sec = request->tv_sec - tp.tv_sec;
! tp.tv_nsec = request->tv_nsec - tp.tv_nsec;
! if (tp.tv_nsec < 0) {
! tp.tv_nsec += POW10_9;
! tp.tv_sec --;
! }
! return nanosleep(&tp, remain);
}
/**
--- 263,353 ----
* with errno set to indicate the error.
*/
int clock_nanosleep(clockid_t clock_id, int flags,
! const struct timespec *request,
! struct timespec *remain)
{
struct timespec tp;
! switch (clock_id) {
! case CLOCK_REALTIME:
! /* TIMER_ABSTIME = 1 */
! if (flags == 0)
! return nanosleep(request, remain);
!
! // sleep until abs time
! clock_gettime(CLOCK_REALTIME, &tp);
!
! tp.tv_sec = request->tv_sec - tp.tv_sec;
! tp.tv_nsec = request->tv_nsec - tp.tv_nsec;
! if (tp.tv_nsec < 0) {
! tp.tv_nsec += POW10_9;
! tp.tv_sec --;
! }
! return nanosleep(&tp, remain);
!
! case CLOCK_MONOTONIC:
! if (request->tv_sec < 0 || request->tv_nsec < 0 || request->tv_nsec
>= POW10_9) {
! return lc_set_errno(EINVAL);
! }
! //
https://docs.microsoft.com/en-us/windows/win32/sync/using-waitable-timer-objects
! // 100ns intervals, Positive values indicate absolute time.
! LARGE_INTEGER liDueTime;
! liDueTime.QuadPart = ((long long)request->tv_sec*POW10_9 +
request->tv_nsec)/100;
! if (flags == 0) {
! liDueTime.QuadPart = -liDueTime.QuadPart;
! } else {
! liDueTime.QuadPart += DELTA_EPOCH_IN_100NS; // TIMER_ABSTIME
! }
!
! // Use high resolution if Windows version is at least Win 2004
! int timerCreateFlags;
! if (haveHighResTimer())
! timerCreateFlags = CREATE_WAITABLE_TIMER_HIGH_RESOLUTION;
! else
! timerCreateFlags = CREATE_WAITABLE_TIMER_MANUAL_RESET;
!
! // Create an unnamed waitable timer.
! HANDLE hTimer = CreateWaitableTimerEx(0, 0, timerCreateFlags,
TIMER_ALL_ACCESS);
! if (!hTimer) {
! return lc_set_errno(ENOTSUP);
! }
!
! struct timespec then;
! if (remain) clock_gettime(CLOCK_MONOTONIC, &then);
!
! // Set a timer
! int retval = 0;
! if (!SetWaitableTimer(hTimer, &liDueTime, 0, 0, 0, 0)) {
! retval = lc_set_errno(ENOTSUP);
! } else {
! // Wait for the timer.
! DWORD st = WaitForSingleObject(hTimer, INFINITE);
! if (st != WAIT_OBJECT_0) {
! retval = lc_set_errno(ENOTSUP);
! }
! }
!
! CloseHandle(hTimer);
!
! struct timespec now, elapsed;
! if (remain) {
! clock_gettime(CLOCK_MONOTONIC, &now);
! timersub(&elapsed, &now, &then);
! timersub(remain, request, &elapsed);
! if (remain->tv_sec < 0) {
! remain->tv_sec = 0;
! remain->tv_nsec = 0;
! }
! }
!
! return retval;
! default:
! return lc_set_errno(EINVAL);
! }
}
/**
***************
*** 278,280 ****
--- 379,488 ----
return 0;
}
+
+ #ifdef UNIT_TEST
+ // gcc -DUNIT_TEST -g -O2 clock_nanosleep.c -o n -Wall -lwinmm
+
+ #include <stdio.h>
+
+ typedef NTSTATUS NTAPI (*NtQueryTimerResolution)(PULONG MinimumResolution,
+ PULONG MaximumResolution,
+ PULONG CurrentResolution);
+
+ // Returns the current timer resolution in 100 ns units (10,000 implies
+ // a one ms timer interval).
+ ULONG GetTimerResolution() {
+ HMODULE ntdll = LoadLibraryA("ntdll.dll");
+ NtQueryTimerResolution QueryTimerResolution
+ = (NtQueryTimerResolution)GetProcAddress(ntdll,
"NtQueryTimerResolution");
+
+ ULONG minimum, maximum, current;
+ QueryTimerResolution(&minimum, &maximum, ¤t);
+ return current;
+ }
+
+ static inline void timeradd(struct timespec* c,
+ const struct timespec* a,
+ const struct timespec* b)
+ {
+ c->tv_sec = a->tv_sec + b->tv_sec;
+ c->tv_nsec = a->tv_nsec + b->tv_nsec;
+ if (c->tv_nsec >= POW10_9) {
+ c->tv_sec++;
+ c->tv_nsec -= POW10_9;
+ }
+ }
+
+ int main(int ac, char**av)
+ {
+ int iterations = 10;
+ struct timespec then, now, elapsed, remain;
+ struct timespec delayTime = { .tv_sec=0, .tv_nsec=1000000 };
+
+ if (ac > 1) delayTime.tv_nsec = atoi(av[1]);
+ if (ac > 2) iterations = atoi(av[2]);
+
+ if (haveHighResTimer())
+ printf("High Res timer\n");
+ else
+ printf("Old windows version: no high res timer\n");
+
+ ULONG ns100 = GetTimerResolution();
+ double timerRes = ns100/10000.;
+ printf("Timer resolution: %.6f ms\n", timerRes);
+
+ TIMECAPS caps; // use -lwinmm
+ int s = timeGetDevCaps(&caps, sizeof(caps));
+ if (s != TIMERR_NOERROR) perror("timeGetDevCaps");
+
+ printf("caps: min %d ms, max %d ms\n", caps.wPeriodMin, caps.wPeriodMax);
+
+ if (caps.wPeriodMin > timerRes) {
+ printf("No reason to set min timer resolution\n");
+ } else {
+ timeBeginPeriod(caps.wPeriodMin);
+ printf("Timer res set to minimum\n");
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &then);
+
+ for (int i=0; i < iterations; i++) {
+ int s = clock_nanosleep(CLOCK_MONOTONIC, 0, &delayTime, &remain);
+ if (s) { perror("clock_nanosleep"); break; }
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ timersub(&elapsed, &now, &then);
+
+ printf("remain %jd sec, %ld ns\n", remain.tv_sec, remain.tv_nsec);
+ printf("elapsed %.7f sec\n", elapsed.tv_sec +
elapsed.tv_nsec*1./POW10_9);
+
+ double dt = delayTime.tv_nsec*1./POW10_9;
+ printf("expected %.7f sec, %d iterations, %.7f sec delay\n",
+ iterations*dt, iterations, dt);
+
+ // wait until absolute time
+ long long waitTime = delayTime.tv_sec * POW10_9 + delayTime.tv_nsec;
+ waitTime *= iterations;
+ delayTime.tv_sec = waitTime/(double)POW10_9;
+ delayTime.tv_nsec = waitTime - delayTime.tv_sec*(long long)POW10_9;
+
+ clock_gettime(CLOCK_MONOTONIC, &then);
+
+ struct timespec until;
+ timeradd(&until, &then, &delayTime);
+
+ s = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &delayTime, &remain);
+ if (s) perror("clock_nanosleep");
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ printf("\n wait until abstime\n");
+ printf("now: %jd sec %ld nsec\n", now.tv_sec, now.tv_nsec);
+ printf("expected: %jd sec %ld nsec\n", until.tv_sec, until.tv_nsec);
+ printf("remain %jd sec, %ld ns\n", remain.tv_sec, remain.tv_nsec);
+
+ return 0;
+ }
+ #endif
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public