diff options
Diffstat (limited to 'winsup/cygwin/times.cc')
-rw-r--r-- | winsup/cygwin/times.cc | 136 |
1 files changed, 104 insertions, 32 deletions
diff --git a/winsup/cygwin/times.cc b/winsup/cygwin/times.cc index c911cfe54..19c03fdf8 100644 --- a/winsup/cygwin/times.cc +++ b/winsup/cygwin/times.cc @@ -666,6 +666,53 @@ hires_ns::nsecs () return now.QuadPart; } +LONGLONG +hires_ms::timeGetTime_ns () +{ + LARGE_INTEGER t; + + /* This is how timeGetTime is implemented in winmm.dll. + The real timeGetTime subtracts and adds some values which are constant + over the lifetime of the process. Since we don't need absolute accuracy + of the value returned by timeGetTime, only relative accuracy, we can skip + this step. However, if we ever find out that we need absolute accuracy, + here's how it works in it's full beauty: + + - At process startup, winmm initializes two calibration values: + + DWORD tick_count_start; + LARGE_INTEGER int_time_start; + do + { + tick_count_start = GetTickCount () + do + { + int_time_start.HighPart = SharedUserData.InterruptTime.High1Time; + int_time_start.LowPart = SharedUserData.InterruptTime.LowPart; + } + while (int_time_start.HighPart + != SharedUserData.InterruptTime.High2Time); + } + while (tick_count_start != GetTickCount (); + + - timeGetTime computes its return value in the loop as below, and then: + + t.QuadPart -= int_time_start.QuadPart; + t.QuadPart /= 10000; + t.LowPart += tick_count_start; + return t.LowPart; + */ + do + { + t.HighPart = SharedUserData.InterruptTime.High1Time; + t.LowPart = SharedUserData.InterruptTime.LowPart; + } + while (t.HighPart != SharedUserData.InterruptTime.High2Time); + /* We use the value in full 100ns resolution in the calling functions + anyway, so we can skip dividing by 10000 here. */ + return t.QuadPart; +} + void hires_ms::prime () { @@ -673,7 +720,7 @@ hires_ms::prime () { int priority = GetThreadPriority (GetCurrentThread ()); SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL); - initime_ns = systime_ns () - (((LONGLONG) timeGetTime ()) * 10000LL); + initime_ns = systime_ns () - timeGetTime_ns (); inited = true; SetThreadPriority (GetCurrentThread (), priority); } @@ -687,12 +734,12 @@ hires_ms::nsecs () prime (); LONGLONG t = systime_ns (); - LONGLONG res = initime_ns + (((LONGLONG) timeGetTime ()) * 10000LL); + LONGLONG res = initime_ns + timeGetTime_ns (); if (res < (t - 40 * 10000LL)) { inited = false; prime (); - res = initime_ns + (((LONGLONG) timeGetTime ()) * 10000LL); + res = initime_ns + timeGetTime_ns (); } return res; } @@ -734,7 +781,7 @@ clock_gettime (clockid_t clk_id, struct timespec *tp) static DWORD minperiod; // FIXME: Maintain period after a fork. LONGLONG -hires_ns::resolution() +hires_ns::resolution () { if (!inited) prime (); @@ -752,24 +799,34 @@ hires_ms::resolution () { if (!minperiod) { - /* Try to empirically determine current timer resolution */ - int priority = GetThreadPriority (GetCurrentThread ()); - SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL); - DWORD period = 0; - for (int i = 0; i < 4; i++) + NTSTATUS status; + ULONG coarsest, finest, actual; + + status = NtQueryTimerResolution (&coarsest, &finest, &actual); + if (NT_SUCCESS (status)) + minperiod = (DWORD) actual / 10000L; + else { - DWORD now; - DWORD then = timeGetTime (); - while ((now = timeGetTime ()) == then) - continue; - then = now; - while ((now = timeGetTime ()) == then) - continue; - period += now - then; + /* Try to empirically determine current timer resolution */ + int priority = GetThreadPriority (GetCurrentThread ()); + SetThreadPriority (GetCurrentThread (), + THREAD_PRIORITY_TIME_CRITICAL); + LONGLONG period = 0; + for (int i = 0; i < 4; i++) + { + LONGLONG now; + LONGLONG then = timeGetTime_ns (); + while ((now = timeGetTime_ns ()) == then) + continue; + then = now; + while ((now = timeGetTime_ns ()) == then) + continue; + period += now - then; + } + SetThreadPriority (GetCurrentThread (), priority); + period /= 40000L; + minperiod = (DWORD) period; } - SetThreadPriority (GetCurrentThread (), priority); - period /= 4; - minperiod = period; } return minperiod; } @@ -807,28 +864,43 @@ extern "C" int clock_setres (clockid_t clk_id, struct timespec *tp) { static NO_COPY bool period_set; + int status; + if (clk_id != CLOCK_REALTIME) { set_errno (EINVAL); return -1; } - if (period_set) - timeEndPeriod (minperiod); - - DWORD period = (tp->tv_sec * 1000) + ((tp->tv_nsec) / 1000000); - - if (timeBeginPeriod (period)) + /* Convert to 100ns to match OS resolution. The OS uses ULONG values + to express resolution in 100ns units, so the coarsest timer resolution + is < 430 secs. Actually the coarsest timer resolution is only slightly + beyond 15ms, but this might change in future OS versions, so we play nice + here. */ + ULONGLONG period = (tp->tv_sec * 10000000ULL) + ((tp->tv_nsec) / 100ULL); + + /* clock_setres is non-POSIX/non-Linux. On QNX, the function always + rounds the incoming value to the nearest supported value. */ + ULONG coarsest, finest, actual; + if (NT_SUCCESS (NtQueryTimerResolution (&coarsest, &finest, &actual))) { - minperiod = period; - period_set = true; + if (period > coarsest) + period = coarsest; + else if (finest > period) + period = finest; } - else + + if (period_set + && NT_SUCCESS (NtSetTimerResolution (minperiod * 10000L, FALSE, &actual))) + period_set = false; + + status = NtSetTimerResolution (period, TRUE, &actual); + if (!NT_SUCCESS (status)) { - __seterrno (); - timeBeginPeriod (minperiod); + __seterrno_from_nt_status (status); return -1; } - + minperiod = actual / 10000L; + period_set = true; return 0; } |