This change implements querying, setting and resetting timer resolution. The set resolution has no effect on any of the wait calls yet - they are still using select() or server_select() with the raw values.
Signed-off-by: Arkadiusz Hiler ahiler@codeweavers.com --- dlls/ntdll/tests/time.c | 32 +++++++++++++++++++++++ dlls/ntdll/unix/sync.c | 57 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 85 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c index a00d507e4e..6c800c8c4e 100644 --- a/dlls/ntdll/tests/time.c +++ b/dlls/ntdll/tests/time.c @@ -35,6 +35,9 @@ static NTSTATUS (WINAPI *pRtlQueryTimeZoneInformation)( RTL_TIME_ZONE_INFORMATIO static NTSTATUS (WINAPI *pRtlQueryDynamicTimeZoneInformation)( RTL_DYNAMIC_TIME_ZONE_INFORMATION *); static BOOL (WINAPI *pRtlQueryUnbiasedInterruptTime)( ULONGLONG *time );
+static NTSTATUS (WINAPI *pNtQueryTimerResolution)( ULONG *min_res, ULONG *max_res, ULONG *current_res ); +static NTSTATUS (WINAPI *pNtSetTimerResolution)( ULONG res, BOOLEAN set, ULONG *current_res ); + static const int MonthLengths[2][12] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, @@ -264,6 +267,32 @@ static void test_user_shared_data_time(void) todo_wine ok(changed >= 90, "tick count isn't updated after sleeping one millisecond (%d%% correct)\n", changed); }
+static void test_timer_resolution(void) +{ + ULONG min, max, cur, def, cur2; + + ok(pNtQueryTimerResolution(NULL, NULL, NULL) == STATUS_ACCESS_VIOLATION, "getting resolution is fine with writign to a NULL pointer\n"); + ok(pNtQueryTimerResolution(&min, &max, &def) == STATUS_SUCCESS, "failed to get resolution\n"); + ok(def == min, "the default timer resolution is not set to the minimum\n"); + + ok(pNtSetTimerResolution(0, TRUE, NULL) == STATUS_ACCESS_VIOLATION, "setting resolution is fine with writing to a NULL pointer\n"); + ok(pNtSetTimerResolution(0, FALSE, &cur) == STATUS_TIMER_RESOLUTION_NOT_SET, "we have managed to unset resolution without setting one\n"); + + ok(pNtSetTimerResolution(20000, TRUE, &cur) == STATUS_SUCCESS, "filed to set timer resolution\n"); + ok(cur <= 20000 && cur >= 20000/2, "resoltuion was set to %u which is not close to the requested one\n", cur); + + ok(pNtQueryTimerResolution(&min, &max, &cur2) == STATUS_SUCCESS, "failed to get resolution\n"); + ok(cur2 == cur, "setting and quering resoltions report mismatched values %u != %u\n", cur2, cur); + + ok(pNtSetTimerResolution(100000, TRUE, &cur) == STATUS_SUCCESS, "failed to decrease resolution\n"); + ok(cur <= 100000 && cur >= 100000/2, "resolution was set to %u which is not close to the requested one\n", cur); + + ok(pNtSetTimerResolution(123456, FALSE, &cur) == STATUS_SUCCESS, "failed to unset resolution\n"); + ok(cur == def, "after unsetting we've got %u instead of the default %u\n", cur, def); + + ok(pNtSetTimerResolution(123456, FALSE, &cur) == STATUS_TIMER_RESOLUTION_NOT_SET, "we have unset resolution more times than one\n"); +} + START_TEST(time) { HMODULE mod = GetModuleHandleA("ntdll.dll"); @@ -276,6 +305,8 @@ START_TEST(time) pRtlQueryDynamicTimeZoneInformation = (void *)GetProcAddress(mod, "RtlQueryDynamicTimeZoneInformation"); pRtlQueryUnbiasedInterruptTime = (void *)GetProcAddress(mod, "RtlQueryUnbiasedInterruptTime"); + pNtQueryTimerResolution = (void *)GetProcAddress(mod, "NtQueryTimerResolution"); + pNtSetTimerResolution = (void *)GetProcAddress(mod, "NtSetTimerResolution");
if (pRtlTimeToTimeFields && pRtlTimeFieldsToTime) test_pRtlTimeToTimeFields(); @@ -284,4 +315,5 @@ START_TEST(time) test_NtQueryPerformanceCounter(); test_RtlQueryTimeZoneInformation(); test_user_shared_data_time(); + test_timer_resolution(); } diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 23dca9c61b..eb93ac0d4b 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -81,6 +81,15 @@ static const LARGE_INTEGER zero_timeout;
static pthread_mutex_t addr_mutex = PTHREAD_MUTEX_INITIALIZER;
+static ULONG timer_resolution_current = 0; +static const ULONG timer_resolution_min = 156250; /* also the default */ +static const ULONG timer_resolution_max = 5000; + +static ULONG get_timer_resolution(void) +{ + return timer_resolution_current ? timer_resolution_current : timer_resolution_min; +} + /* return a monotonic time counter, in Win32 ticks */ static inline ULONGLONG monotonic_counter(void) { @@ -1438,8 +1447,19 @@ NTSTATUS WINAPI NtSetSystemTime( const LARGE_INTEGER *new, LARGE_INTEGER *old ) */ NTSTATUS WINAPI NtQueryTimerResolution( ULONG *min_res, ULONG *max_res, ULONG *current_res ) { - FIXME( "(%p,%p,%p), stub!\n", min_res, max_res, current_res ); - return STATUS_NOT_IMPLEMENTED; + __TRY + { + *min_res = timer_resolution_min; + *max_res = timer_resolution_max; + *current_res = get_timer_resolution(); + } + __EXCEPT_PAGE_FAULT + { + return STATUS_ACCESS_VIOLATION; + } + __ENDTRY + + return STATUS_SUCCESS; }
@@ -1448,8 +1468,37 @@ NTSTATUS WINAPI NtQueryTimerResolution( ULONG *min_res, ULONG *max_res, ULONG *c */ NTSTATUS WINAPI NtSetTimerResolution( ULONG res, BOOLEAN set, ULONG *current_res ) { - FIXME( "(%u,%u,%p), stub!\n", res, set, current_res ); - return STATUS_NOT_IMPLEMENTED; + __TRY + { + *current_res = timer_resolution_current; + } + __EXCEPT_PAGE_FAULT + { + return STATUS_ACCESS_VIOLATION; + } + __ENDTRY + + if (set) + { + if (res < timer_resolution_max) + res = timer_resolution_max; + + if (res > timer_resolution_min) + res = timer_resolution_min; + + InterlockedExchange( (LONG*) &timer_resolution_current, res ); + } + else + { + ULONG original = InterlockedExchange( (LONG*) &timer_resolution_current, 0 ); + + if (original == 0) + return STATUS_TIMER_RESOLUTION_NOT_SET; + } + + *current_res = get_timer_resolution(); + + return STATUS_SUCCESS; }