Also defend against timer resolution changes during the test. Reduce the test duration a bit.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51533 Signed-off-by: Francois Gouget fgouget@codeweavers.com --- v2: The timer resolution change and test duration. v3: No change.
This should fix the failures on cw-rx460 when the Radeon driver sets the timer resolution to 2 ms. --- dlls/kernelbase/tests/sync.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/dlls/kernelbase/tests/sync.c b/dlls/kernelbase/tests/sync.c index 50ce62602b0..8127b4238b9 100644 --- a/dlls/kernelbase/tests/sync.c +++ b/dlls/kernelbase/tests/sync.c @@ -19,10 +19,14 @@ */
#include <stdarg.h> +#include <stdlib.h> + +#include <ntstatus.h> +#define WIN32_NO_STATUS #include <windef.h> #include <winbase.h> -#include <stdlib.h> #include <winerror.h> +#include <winternl.h>
#include "wine/test.h"
@@ -176,7 +180,9 @@ static void test_Sleep(void) { LARGE_INTEGER frequency; LARGE_INTEGER t1, t2; - double elapsed_time; + double elapsed_time, min, max; + ULONG dummy, r1, r2; + NTSTATUS status; BOOL ret; int i;
@@ -186,15 +192,30 @@ static void test_Sleep(void) ret = QueryPerformanceCounter(&t1); ok(ret, "QueryPerformanceCounter failed\n");
- for (i = 0; i < 100; i++) { + /* Get the timer resolution before... */ + r1 = 156250; + status = NtQueryTimerResolution(&dummy, &dummy, &r1); + todo_wine ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status); + + for (i = 0; i < 50; i++) { Sleep(1); }
ret = QueryPerformanceCounter(&t2); ok(ret, "QueryPerformanceCounter failed\n");
+ /* ...and after in case some other process changes it during this test */ + r2 = 156250; + status = NtQueryTimerResolution(&dummy, &dummy, &r2); + todo_wine ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status); + elapsed_time = (t2.QuadPart - t1.QuadPart) / (double)frequency.QuadPart; - todo_wine ok(elapsed_time >= 1.5 && elapsed_time <= 4.0, "got %f\n", elapsed_time); + min = 50.0 * (r1 < r2 ? r1 : r2) / 10000000.0; + max = 50.0 * (r1 < r2 ? r2 : r1) / 10000000.0; + + /* Add an extra 1s to account for potential scheduling delays */ + todo_wine ok(0.9 * min <= elapsed_time && elapsed_time <= 1.0 + max, + "got %f, expected between %f and %f\n", elapsed_time, min, max); }
START_TEST(sync)
Signed-off-by: Francois Gouget fgouget@codeweavers.com --- v2: No change since v1. --- dlls/ntdll/tests/time.c | 110 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+)
diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c index ee683e9a0aa..5f9c49989d8 100644 --- a/dlls/ntdll/tests/time.c +++ b/dlls/ntdll/tests/time.c @@ -216,6 +216,115 @@ static void test_RtlQueryPerformanceCounter(void) } #endif
+#define CHECK_CURRENT_TIMER(expected) \ + do { \ + todo_wine ok(status == STATUS_SUCCESS, "NtSetTimerResolution failed %x\n", status); \ + todo_wine ok(cur2 == (expected), "expected new timer resolution %u, got %u\n", (expected), cur2); \ + min2 = min + 10; \ + cur2 = min2 + 1; \ + max2 = cur2 + 1; \ + status = NtQueryTimerResolution(&min2, &max2, &cur2); \ + todo_wine ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed %x\n", status); \ + todo_wine ok(min2 == min, "NtQueryTimerResolution() expected min=%u, got %u\n", min, min2); \ + todo_wine ok(max2 == max, "NtQueryTimerResolution() expected max=%u, got %u\n", max, max2); \ + todo_wine ok(cur2 == expected, "NtQueryTimerResolution() expected timer resolution %u, got %u\n", (expected), cur2); \ + } while (0) + +static void test_TimerResolution(void) +{ + ULONG min, max, cur, min2, max2, cur2, set; + NTSTATUS status; + + status = NtQueryTimerResolution(NULL, &max, &cur); + todo_wine ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(NULL,,) success\n"); + + status = NtQueryTimerResolution(&min, NULL, &cur); + todo_wine ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(,NULL,) success\n"); + + status = NtQueryTimerResolution(&min, &max, NULL); + todo_wine ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(,,NULL) success\n"); + + min = 212121; + cur = min + 1; + max = cur + 1; + status = NtQueryTimerResolution(&min, &max, &cur); + todo_wine ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status); + todo_wine ok(min == 156250 /* 1/64s HPET */ || min == 156001 /* RTC */, + "unexpected minimum timer resolution %u\n", min); + ok(0 < max, "invalid maximum timer resolution, should be 0 < %u\n", max); + todo_wine ok(max <= cur && cur <= min, "invalid timer resolutions, should be %u <= %u <= %u\n", max, cur, min); + + status = NtSetTimerResolution(0, FALSE, NULL); + todo_wine ok(status == STATUS_ACCESS_VIOLATION, "NtSetTimerResolution(,,NULL) success\n"); + + /* Nothing happens if that pointer is not good */ + status = NtSetTimerResolution(cur - 1, TRUE, NULL); + todo_wine ok(status == STATUS_ACCESS_VIOLATION, "NtSetTimerResolution() failed %x\n", status); + + min2 = min + 1; + cur2 = min2 + 1; + max2 = cur2 + 1; + status = NtQueryTimerResolution(&min2, &max2, &cur2); + todo_wine ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status); + todo_wine ok(min2 == min, "NtQueryTimerResolution() expected min=%u, got %u\n", min, min2); + todo_wine ok(max2 == max, "NtQueryTimerResolution() expected max=%u, got %u\n", max, max2); + todo_wine ok(cur2 == cur, "NtQueryTimerResolution() expected timer resolution %u, got %u\n", cur, cur2); + + /* 'fails' until the first valid timer resolution request */ + cur2 = 7654321; + status = NtSetTimerResolution(0, FALSE, &cur2); + todo_wine ok(status == STATUS_TIMER_RESOLUTION_NOT_SET, "NtSetTimerResolution() failed %x\n", status); + /* and returns the current timer resolution */ + todo_wine ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", cur, cur2); + + + cur2 = 7654321; + status = NtSetTimerResolution(max - 1, TRUE, &cur2); + CHECK_CURRENT_TIMER(max); + + /* Rescinds our timer resolution request */ + cur2 = 7654321; + status = NtSetTimerResolution(0, FALSE, &cur2); + todo_wine ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); + /* -> the timer resolution was reset to its initial value */ + todo_wine ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", min, cur2); + + cur2 = 7654321; + status = NtSetTimerResolution(0, FALSE, &cur2); + todo_wine ok(status == STATUS_TIMER_RESOLUTION_NOT_SET, "NtSetTimerResolution() failed %x\n", status); + todo_wine ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", cur, cur2); + + cur2 = 7654321; + status = NtSetTimerResolution(min + 1, TRUE, &cur2); + todo_wine ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); + /* This works because: + * - Either cur is the minimum (15.6 ms) resolution already, i.e. the + * closest valid value 'set' is rounded to. + * - Or some other application requested a higher timer resolution, cur, + * and any attempt to lower the resolution has no effect until that + * request is rescinded (hopefully after this test is done). + */ + CHECK_CURRENT_TIMER(cur); + + /* The requested resolution may (win7) or may not be rounded */ + cur2 = 7654321; + set = max < cur ? cur - 1 : max; + status = NtSetTimerResolution(set, TRUE, &cur2); + todo_wine ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); + todo_wine ok(cur2 <= set, "expected new timer resolution %u <= %u\n", cur2, set); + trace("timer resolution: %u(max) <= %u(cur) <= %u(prev) <= %u(min)\n", max, cur2, cur, min); + + cur2 = 7654321; + status = NtSetTimerResolution(cur + 1, TRUE, &cur2); + CHECK_CURRENT_TIMER(cur); /* see min + 1 test */ + + /* Cleanup by rescinding the last request */ + cur2 = 7654321; + status = NtSetTimerResolution(0, FALSE, &cur2); + todo_wine ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); + todo_wine ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", set, cur2); +} + static void test_RtlQueryTimeZoneInformation(void) { RTL_DYNAMIC_TIME_ZONE_INFORMATION tzinfo, tzinfo2; @@ -379,4 +488,5 @@ START_TEST(time) #if defined(__i386__) || defined(__x86_64__) test_RtlQueryPerformanceCounter(); #endif + test_TimerResolution(); }
Signed-off-by: Francois Gouget fgouget@codeweavers.com --- v2: Forgot a todo_wine in kernelbase:sync. v3: Removed the todo_wine in kernelbase:sync. --- dlls/kernelbase/tests/sync.c | 8 ++--- dlls/ntdll/tests/time.c | 60 ++++++++++++++++++------------------ dlls/ntdll/unix/sync.c | 33 +++++++++++++++++--- 3 files changed, 63 insertions(+), 38 deletions(-)
diff --git a/dlls/kernelbase/tests/sync.c b/dlls/kernelbase/tests/sync.c index 8127b4238b9..acbfdeff0b7 100644 --- a/dlls/kernelbase/tests/sync.c +++ b/dlls/kernelbase/tests/sync.c @@ -195,7 +195,7 @@ static void test_Sleep(void) /* Get the timer resolution before... */ r1 = 156250; status = NtQueryTimerResolution(&dummy, &dummy, &r1); - todo_wine ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status); + ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status);
for (i = 0; i < 50; i++) { Sleep(1); @@ -207,15 +207,15 @@ static void test_Sleep(void) /* ...and after in case some other process changes it during this test */ r2 = 156250; status = NtQueryTimerResolution(&dummy, &dummy, &r2); - todo_wine ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status); + ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status);
elapsed_time = (t2.QuadPart - t1.QuadPart) / (double)frequency.QuadPart; min = 50.0 * (r1 < r2 ? r1 : r2) / 10000000.0; max = 50.0 * (r1 < r2 ? r2 : r1) / 10000000.0;
/* Add an extra 1s to account for potential scheduling delays */ - todo_wine ok(0.9 * min <= elapsed_time && elapsed_time <= 1.0 + max, - "got %f, expected between %f and %f\n", elapsed_time, min, max); + ok(0.9 * min <= elapsed_time && elapsed_time <= 1.0 + max, + "got %f, expected between %f and %f\n", elapsed_time, min, max); }
START_TEST(sync) diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c index 5f9c49989d8..f75b159f441 100644 --- a/dlls/ntdll/tests/time.c +++ b/dlls/ntdll/tests/time.c @@ -218,16 +218,16 @@ static void test_RtlQueryPerformanceCounter(void)
#define CHECK_CURRENT_TIMER(expected) \ do { \ - todo_wine ok(status == STATUS_SUCCESS, "NtSetTimerResolution failed %x\n", status); \ - todo_wine ok(cur2 == (expected), "expected new timer resolution %u, got %u\n", (expected), cur2); \ + ok(status == STATUS_SUCCESS, "NtSetTimerResolution failed %x\n", status); \ + ok(cur2 == (expected), "expected new timer resolution %u, got %u\n", (expected), cur2); \ min2 = min + 10; \ cur2 = min2 + 1; \ max2 = cur2 + 1; \ status = NtQueryTimerResolution(&min2, &max2, &cur2); \ - todo_wine ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed %x\n", status); \ - todo_wine ok(min2 == min, "NtQueryTimerResolution() expected min=%u, got %u\n", min, min2); \ - todo_wine ok(max2 == max, "NtQueryTimerResolution() expected max=%u, got %u\n", max, max2); \ - todo_wine ok(cur2 == expected, "NtQueryTimerResolution() expected timer resolution %u, got %u\n", (expected), cur2); \ + ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed %x\n", status); \ + ok(min2 == min, "NtQueryTimerResolution() expected min=%u, got %u\n", min, min2); \ + ok(max2 == max, "NtQueryTimerResolution() expected max=%u, got %u\n", max, max2); \ + ok(cur2 == expected, "NtQueryTimerResolution() expected timer resolution %u, got %u\n", (expected), cur2); \ } while (0)
static void test_TimerResolution(void) @@ -236,46 +236,46 @@ static void test_TimerResolution(void) NTSTATUS status;
status = NtQueryTimerResolution(NULL, &max, &cur); - todo_wine ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(NULL,,) success\n"); + ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(NULL,,) success\n");
status = NtQueryTimerResolution(&min, NULL, &cur); - todo_wine ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(,NULL,) success\n"); + ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(,NULL,) success\n");
status = NtQueryTimerResolution(&min, &max, NULL); - todo_wine ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(,,NULL) success\n"); + ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(,,NULL) success\n");
min = 212121; cur = min + 1; max = cur + 1; status = NtQueryTimerResolution(&min, &max, &cur); - todo_wine ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status); - todo_wine ok(min == 156250 /* 1/64s HPET */ || min == 156001 /* RTC */, - "unexpected minimum timer resolution %u\n", min); + ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status); + ok(min == 156250 /* 1/64s HPET */ || min == 156001 /* RTC */, + "unexpected minimum timer resolution %u\n", min); ok(0 < max, "invalid maximum timer resolution, should be 0 < %u\n", max); - todo_wine ok(max <= cur && cur <= min, "invalid timer resolutions, should be %u <= %u <= %u\n", max, cur, min); + ok(max <= cur && cur <= min, "invalid timer resolutions, should be %u <= %u <= %u\n", max, cur, min);
status = NtSetTimerResolution(0, FALSE, NULL); - todo_wine ok(status == STATUS_ACCESS_VIOLATION, "NtSetTimerResolution(,,NULL) success\n"); + ok(status == STATUS_ACCESS_VIOLATION, "NtSetTimerResolution(,,NULL) success\n");
/* Nothing happens if that pointer is not good */ status = NtSetTimerResolution(cur - 1, TRUE, NULL); - todo_wine ok(status == STATUS_ACCESS_VIOLATION, "NtSetTimerResolution() failed %x\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "NtSetTimerResolution() failed %x\n", status);
min2 = min + 1; cur2 = min2 + 1; max2 = cur2 + 1; status = NtQueryTimerResolution(&min2, &max2, &cur2); - todo_wine ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status); - todo_wine ok(min2 == min, "NtQueryTimerResolution() expected min=%u, got %u\n", min, min2); - todo_wine ok(max2 == max, "NtQueryTimerResolution() expected max=%u, got %u\n", max, max2); - todo_wine ok(cur2 == cur, "NtQueryTimerResolution() expected timer resolution %u, got %u\n", cur, cur2); + ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%x)\n", status); + ok(min2 == min, "NtQueryTimerResolution() expected min=%u, got %u\n", min, min2); + ok(max2 == max, "NtQueryTimerResolution() expected max=%u, got %u\n", max, max2); + ok(cur2 == cur, "NtQueryTimerResolution() expected timer resolution %u, got %u\n", cur, cur2);
/* 'fails' until the first valid timer resolution request */ cur2 = 7654321; status = NtSetTimerResolution(0, FALSE, &cur2); - todo_wine ok(status == STATUS_TIMER_RESOLUTION_NOT_SET, "NtSetTimerResolution() failed %x\n", status); + ok(status == STATUS_TIMER_RESOLUTION_NOT_SET, "NtSetTimerResolution() failed %x\n", status); /* and returns the current timer resolution */ - todo_wine ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", cur, cur2); + ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", cur, cur2);
cur2 = 7654321; @@ -285,18 +285,18 @@ static void test_TimerResolution(void) /* Rescinds our timer resolution request */ cur2 = 7654321; status = NtSetTimerResolution(0, FALSE, &cur2); - todo_wine ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); + ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); /* -> the timer resolution was reset to its initial value */ - todo_wine ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", min, cur2); + ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", min, cur2);
cur2 = 7654321; status = NtSetTimerResolution(0, FALSE, &cur2); - todo_wine ok(status == STATUS_TIMER_RESOLUTION_NOT_SET, "NtSetTimerResolution() failed %x\n", status); - todo_wine ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", cur, cur2); + ok(status == STATUS_TIMER_RESOLUTION_NOT_SET, "NtSetTimerResolution() failed %x\n", status); + ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", cur, cur2);
cur2 = 7654321; status = NtSetTimerResolution(min + 1, TRUE, &cur2); - todo_wine ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); + ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); /* This works because: * - Either cur is the minimum (15.6 ms) resolution already, i.e. the * closest valid value 'set' is rounded to. @@ -310,8 +310,8 @@ static void test_TimerResolution(void) cur2 = 7654321; set = max < cur ? cur - 1 : max; status = NtSetTimerResolution(set, TRUE, &cur2); - todo_wine ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); - todo_wine ok(cur2 <= set, "expected new timer resolution %u <= %u\n", cur2, set); + ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); + ok(cur2 <= set, "expected new timer resolution %u <= %u\n", cur2, set); trace("timer resolution: %u(max) <= %u(cur) <= %u(prev) <= %u(min)\n", max, cur2, cur, min);
cur2 = 7654321; @@ -321,8 +321,8 @@ static void test_TimerResolution(void) /* Cleanup by rescinding the last request */ cur2 = 7654321; status = NtSetTimerResolution(0, FALSE, &cur2); - todo_wine ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); - todo_wine ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", set, cur2); + ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %x\n", status); + ok(cur2 == cur, "expected requested timer resolution %u, got %u\n", set, cur2); }
static void test_RtlQueryTimeZoneInformation(void) diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 7bdce91e034..d839195fd07 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -1599,8 +1599,13 @@ 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; + TRACE( "(%p,%p,%p)\n", min_res, max_res, current_res ); + if (!min_res || !max_res || !current_res) + return STATUS_ACCESS_VIOLATION; + + *max_res = *current_res = 10000; /* See NtSetTimerResolution() */ + *min_res = 156250; + return STATUS_SUCCESS; }
@@ -1609,8 +1614,28 @@ 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; + static BOOL has_request = FALSE; + TRACE( "(%u,%u,%p), semi-stub!\n", res, set, current_res ); + + if (!current_res) + return STATUS_ACCESS_VIOLATION; + + /* Wine has no support for anything other that 1 ms and does not keep of + * track resolution requests anyway. + * Fortunately NtSetTimerResolution() should ignore requests to lower the + * timer resolution. So by claiming that 'some other process' requested the + * max resolution already, there no need to actually change it. + */ + *current_res = 10000; + + /* Just keep track of whether this process requested a specific timer + * resolution. + */ + if (!has_request && !set) + return STATUS_TIMER_RESOLUTION_NOT_SET; + has_request = set; + + return STATUS_SUCCESS; }