diff options
-rw-r--r-- | winsup/cygwin/ChangeLog | 24 | ||||
-rw-r--r-- | winsup/cygwin/autoload.cc | 38 | ||||
-rw-r--r-- | winsup/cygwin/hires.h | 3 | ||||
-rw-r--r-- | winsup/cygwin/ntdll.h | 18 | ||||
-rw-r--r-- | winsup/cygwin/path.cc | 5 | ||||
-rw-r--r-- | winsup/cygwin/times.cc | 136 |
6 files changed, 164 insertions, 60 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 9a05e8d9f..008bef9a0 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,27 @@ +2011-03-29 Corinna Vinschen <corinna@vinschen.de> + + * 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. + 2011-03-29 Yaakov Selkowitz <yselkowitz@users.sourceforge.net> Corinna Vinschen <corinna@vinschen.de> diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index 3cb68bafe..c0ab2ed26 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -455,27 +455,23 @@ LoadDLLfunc (SetProcessWindowStation, 4, user32) LoadDLLfunc (SetThreadDesktop, 4, user32) LoadDLLfunc (ShowWindowAsync, 8, user32) -LoadDLLfuncEx3 (timeBeginPeriod, 4, winmm, 0, 0, 1) -LoadDLLfuncEx3 (timeEndPeriod, 4, winmm, 0, 0, 1) -LoadDLLfuncEx3 (timeGetDevCaps, 8, winmm, 0, 0, 1) -LoadDLLfuncEx3 (timeGetTime, 0, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveInAddBuffer, 12, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveInClose, 4, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveInGetNumDevs, 0, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveInOpen, 24, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveInPrepareHeader, 12, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveInReset, 4, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveInStart, 4, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveInUnprepareHeader, 12, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveOutClose, 4, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveOutGetNumDevs, 0, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveOutGetVolume, 8, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveOutOpen, 24, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveOutPrepareHeader, 12, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveOutReset, 4, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveOutSetVolume, 8, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveOutUnprepareHeader, 12, winmm, 0, 0, 1) -LoadDLLfuncEx3 (waveOutWrite, 12, winmm, 0, 0, 1) +LoadDLLfuncEx3 (waveInAddBuffer, 12, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveInClose, 4, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveInGetNumDevs, 0, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveInOpen, 24, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveInPrepareHeader, 12, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveInReset, 4, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveInStart, 4, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveInUnprepareHeader, 12, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveOutClose, 4, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveOutGetNumDevs, 0, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveOutGetVolume, 8, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveOutOpen, 24, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveOutPrepareHeader, 12, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveOutReset, 4, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveOutSetVolume, 8, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveOutUnprepareHeader, 12, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveOutWrite, 12, winmm, 1, 0, 1) LoadDLLfunc (accept, 12, ws2_32) LoadDLLfunc (bind, 12, ws2_32) diff --git a/winsup/cygwin/hires.h b/winsup/cygwin/hires.h index 5f87453d6..42151197f 100644 --- a/winsup/cygwin/hires.h +++ b/winsup/cygwin/hires.h @@ -43,12 +43,13 @@ class hires_ns : public hires_base class hires_ms : public hires_base { LONGLONG initime_ns; + LONGLONG timeGetTime_ns (); void prime (); public: LONGLONG nsecs (); LONGLONG usecs () {return nsecs () / 10LL;} LONGLONG msecs () {return nsecs () / 10000LL;} - UINT dmsecs () { return timeGetTime (); } + UINT dmsecs () { return timeGetTime_ns () / 10000LL; } UINT resolution (); LONGLONG uptime () {return (nsecs () - initime_ns) / 10000LL;} }; diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 54c56f027..cd8e1f106 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -599,9 +599,18 @@ typedef struct _TEB /* A lot more follows... */ } TEB, *PTEB; +typedef struct _KSYSTEM_TIME +{ + ULONG LowPart; + LONG High1Time; + LONG High2Time; +} KSYSTEM_TIME, *PKSYSTEM_TIME; + typedef struct _KUSER_SHARED_DATA { - BYTE Reserved1[0x2dc]; + BYTE Reserved1[0x08]; + KSYSTEM_TIME InterruptTime; + BYTE Reserved2[0x2c8]; ULONG DismountCount; /* A lot more follows... */ } KUSER_SHARED_DATA, *PKUSER_SHARED_DATA; @@ -889,6 +898,11 @@ typedef enum _EVENT_INFORMATION_CLASS #define NtCurrentProcess() ((HANDLE) 0xffffffff) #define NtCurrentThread() ((HANDLE) 0xfffffffe) +/* This is the mapping of the KUSER_SHARED_DATA structure into the 32 bit + user address space. We need it here to access the current DismountCount. */ +static KUSER_SHARED_DATA &SharedUserData + = *(volatile PKUSER_SHARED_DATA) 0x7ffe0000; + extern "C" { NTSTATUS NTAPI NtAdjustPrivilegesToken (HANDLE, BOOLEAN, PTOKEN_PRIVILEGES, @@ -970,6 +984,7 @@ extern "C" NTSTATUS NTAPI NtQuerySecurityObject (HANDLE, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, ULONG, PULONG); NTSTATUS NTAPI NtQuerySymbolicLinkObject (HANDLE, PUNICODE_STRING, PULONG); + NTSTATUS NTAPI NtQueryTimerResolution (PULONG, PULONG, PULONG); NTSTATUS NTAPI NtQueryVirtualMemory (HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, ULONG, PULONG); NTSTATUS NTAPI NtQueryVolumeInformationFile (HANDLE, IO_STATUS_BLOCK *, @@ -984,6 +999,7 @@ extern "C" FILE_INFORMATION_CLASS); NTSTATUS NTAPI NtSetSecurityObject (HANDLE, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR); + NTSTATUS NTAPI NtSetTimerResolution (ULONG, BOOLEAN, PULONG); NTSTATUS NTAPI NtUnlockVirtualMemory (HANDLE, PVOID *, ULONG *, ULONG); NTSTATUS NTAPI NtUnmapViewOfSection (HANDLE, PVOID); NTSTATUS NTAPI NtWriteFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 4a7c11692..3a74dfa6a 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -3460,11 +3460,6 @@ static PFAST_CWD *fast_cwd_ptr static int fast_cwd_version __attribute__((section (".cygwin_dll_common"), shared)) = 1; -/* This is the mapping of the KUSER_SHARED_DATA structure into the 32 bit - user address space. We need it here to access the current DismountCount. */ -static KUSER_SHARED_DATA &SharedUserData - = *(volatile PKUSER_SHARED_DATA) 0x7ffe0000; - #define peek32(x) (*(uint32_t *)(x)) /* This function scans the code in ntdll.dll to find the address of the 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; } |