[PATCH 3/3] winmm: Test that timeBeginPeriod(1) works.

Arkadiusz Hiler ahiler at codeweavers.com
Tue Aug 18 08:27:17 CDT 2020


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 at 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();
 }
-- 
2.28.0




More information about the wine-devel mailing list