Timer resolution is a global property on Windows. If one process requests high resolution (e.g. 1ms update interval) via timeBeginPeriod() all the other processes get that effective resolution.
Wine defaults to 1ms, as if there is a process running that has called timeBeginPeriod(1), which is a quite common situation on Windows.
The tests ensures that timeGetTime(), and some of the "wait" calls that are affected by resolution work with our default to make regressions easier to spot.
Signed-off-by: Arkadiusz Hiler ahiler@codeweavers.com --- dlls/winmm/tests/timer.c | 88 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+)
diff --git a/dlls/winmm/tests/timer.c b/dlls/winmm/tests/timer.c index be63f6b032..490bb2dbd7 100644 --- a/dlls/winmm/tests/timer.c +++ b/dlls/winmm/tests/timer.c @@ -193,6 +193,92 @@ static void test_priority(void) timeKillEvent(id); }
+#define IS_1MS(call) \ +{ \ + LARGE_INTEGER _counter[101]; \ + LARGE_INTEGER _frequency; \ + int _hits = 0; \ + \ + QueryPerformanceFrequency(&_frequency); \ + QueryPerformanceCounter(&_counter[0]); \ + for (int _i = 1; _i < ARRAYSIZE(_counter); _i++) \ + { \ + (call); \ + QueryPerformanceCounter(&_counter[_i]); \ + } \ + \ + for (int _i = 1; _i < ARRAYSIZE(_counter); _i++) \ + { \ + ULONGLONG _delta = _counter[_i].QuadPart - _counter[_i-1].QuadPart; \ + _delta = _delta * 10000000 / _frequency.QuadPart; \ + if (5000 < _delta && 20000 > _delta) \ + _hits++; \ + } \ + \ + /* windows has noisy waits, 20% is good enough */ \ + ok(_hits >= ((2*ARRAYSIZE(_counter)-1)/10), \ + #call " hit the expected 0.5ms - 2ms interval only %u/%u times\n", \ + _hits, ARRAYSIZE(_counter)-1); \ +} + +static BOOL spin_the_thread; + +static DWORD WINAPI spinner_thread(void *arg) +{ + while (spin_the_thread) + Sleep(1); + + return 0; +} + +static void test_timer_resolution(void) +{ + /* ensure 1ms resolution on Windows, Wine has that by default */ + ok(timeBeginPeriod(1) == TIMERR_NOERROR, "failed to timeBeginPeriod(1)\n"); + + { + DWORD time, prev; + int count = 0, correct = 0; + + prev = timeGetTime(); + while (count < 100) { + time = timeGetTime(); + if (time != prev) { + if (time - prev == 1) correct++; + prev = time; + count++; + } + } + + ok(correct > (8*count)/10, "timeGetTime() incremented more than 1 too many (%d/%d) times\n", count - correct, count); + } + + { + DWORD thread_id; + HANDLE thread; + HANDLE event = CreateEventA(NULL, TRUE, FALSE, NULL); + + spin_the_thread = TRUE; + thread = CreateThread(NULL, 0, spinner_thread, NULL, 0, &thread_id); + + ok(thread != NULL, "failed to start a thread to wait on\n"); + ok(event != NULL, "failed to create an event\n"); + + IS_1MS(Sleep(1)); + IS_1MS(WaitForSingleObject(thread, 1)); + IS_1MS(WaitForMultipleObjects(1, &thread, TRUE, 1)); + IS_1MS({ + SignalObjectAndWait(event, thread, TRUE, 1); + ResetEvent(event); + }); + + spin_the_thread = FALSE; + ok(WaitForSingleObject(thread, 50) == WAIT_OBJECT_0, "failed to stop the spinner thread\n"); + } + + ok(timeEndPeriod(1) == TIMERR_NOERROR, "failed to timeEndPeriod(1)\n"); +} + START_TEST(timer) { test_timeGetDevCaps(); @@ -216,4 +302,6 @@ START_TEST(timer) }
test_priority(); + + test_timer_resolution(); }