summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/times.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/times.cc')
-rw-r--r--winsup/cygwin/times.cc136
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;
}