summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/times.cc
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2011-03-29 10:21:30 +0000
committerCorinna Vinschen <corinna@vinschen.de>2011-03-29 10:21:30 +0000
commitcd50649255074f7d76400127ed7613d78c08488b (patch)
tree0a024d44927d7dad724cb093591ad8fc4bf32d80 /winsup/cygwin/times.cc
parent700c641dfa72b292a231810ab35d93849f4c8fe1 (diff)
downloadcygnal-cd50649255074f7d76400127ed7613d78c08488b.tar.gz
cygnal-cd50649255074f7d76400127ed7613d78c08488b.tar.bz2
cygnal-cd50649255074f7d76400127ed7613d78c08488b.zip
* autoload.cc (winmm): Remove time functions. Don't treat
unloadable wave functions as fatal. * hires.h (hires_ms::timeGetTime_ns): New private method. (hires_ms::dmsecs): Call timeGetTime_ns here. * ntdll.h (struct _KSYSTEM_TIME): Define. (KUSER_SHARED_DATA): Redefine to allow access to InterruptTime. (SharedUserData): Define here. (NtQueryTimerResolution): Declare. (NtSetTimerResolution): Declare. * path.cc (SharedUserData): Move to ntdll.h. * times.cc (hires_ms::timeGetTime_ns): New private method. Use throughout instead of timeGetTime. Document entire functionality of timeGetTime in case we need it. (hires_ms::resolution): Try a call to NtQueryTimerResolution to fetch current period. Fall back to heuristic if that fails. Cast to DWORD in assignments to minperiod. (clock_setres): Align period to possible values per a call to NtQueryTimerResolution. Explain why. Replace calls to timeBeginPeriod and timeEndPeriod with underlying call to NtSetTimerResolution. Use status code from NtSetTimerResolution to compute errno. Convert period to ULONGLONG and store 100ns value to simplify code.
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;
}