[PATCH 5/5] winmm: Implement timeBeginPeriod() and timeEndPeriod()

Arkadiusz Hiler ahiler at codeweavers.com
Mon Aug 17 12:46:54 CDT 2020


On top of NtSetTimerResolution()...

missing: thread safety

Signed-off-by: Arkadiusz Hiler <ahiler at codeweavers.com>
---
 dlls/winmm/tests/timer.c | 46 +++++++++++++++++++++++-
 dlls/winmm/time.c        | 76 +++++++++++++++++++++++++++++++++-------
 2 files changed, 109 insertions(+), 13 deletions(-)

diff --git a/dlls/winmm/tests/timer.c b/dlls/winmm/tests/timer.c
index be63f6b032..7619aa4a85 100644
--- a/dlls/winmm/tests/timer.c
+++ b/dlls/winmm/tests/timer.c
@@ -23,14 +23,19 @@
 #include <stdlib.h>
 #include <math.h>
 
-#include "wine/test.h"
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#define NONAMELESSUNION
 #include "windef.h"
 #include "winbase.h"
+#include "winternl.h"
 #include "winnls.h"
 #include "mmsystem.h"
 #define NOBITMAP
 #include "mmreg.h"
 
+#include "wine/test.h"
+
 #include "winmm_test.h"
 
 static TIMECAPS tc;
@@ -75,6 +80,43 @@ static void CALLBACK testTimeProc(UINT uID, UINT uMsg, DWORD_PTR dwUser,
         times[count++] = timeGetTime();
 }
 
+static void test_setting_resolution(void)
+{
+    ULONG min, max, cur;
+
+    ok(timeEndPeriod(5) == TIMERR_NOCANDO, "succeded to end period even though we haven't begun it\n");
+    ok(timeBeginPeriod(0) == TIMERR_NOCANDO, "succeded to start illegal 0 period\n");
+    ok(timeEndPeriod(17) == TIMERR_NOERROR, "failed to end period out of 1-15 range (it should always succeed)\n");
+
+    ok(timeBeginPeriod(10) == TIMERR_NOERROR, "failed to begin a period\n");
+    ok(NtQueryTimerResolution(&min, &max, &cur) == STATUS_SUCCESS, "failed to query timer resolution\n");
+    ok(cur <= 100000 && cur >= 50000, "resolution hasn't changed to the desired one\n");
+
+    ok(timeBeginPeriod(5) == TIMERR_NOERROR, "failed to begin a period\n");
+    ok(timeBeginPeriod(4) == TIMERR_NOERROR, "failed to begin a period\n");
+    ok(timeBeginPeriod(3) == TIMERR_NOERROR, "failed to begin a period\n");
+    ok(NtQueryTimerResolution(&min, &max, &cur) == STATUS_SUCCESS, "failed to query timer resolution\n");
+    ok(cur <= 30000, "resolution hasn't changed to the desired one\n");
+
+    ok(timeEndPeriod(3) == TIMERR_NOERROR, "failed to end a period\n");
+    ok(timeEndPeriod(4) == TIMERR_NOERROR, "failed to end a period 4 out of order\n");
+    ok(timeEndPeriod(5) == TIMERR_NOERROR, "failed to end a period\n");
+    ok(NtQueryTimerResolution(&min, &max, &cur) == STATUS_SUCCESS, "failed to query timer resolution\n");
+    ok(cur <= 100000 && cur >= 50000, "resolution hasn't changed to the desired one\n");
+
+    ok(timeEndPeriod(10) == TIMERR_NOERROR, "failed to end a period 7 out of order\n");
+
+    ok(NtQueryTimerResolution(&min, &max, &cur) == STATUS_SUCCESS, "failed to query timer resolution\n");
+    ok(cur == min, "resolution is not back to default after ending all periods\n");
+
+    ok(timeBeginPeriod(16) == TIMERR_NOERROR, "failed to begin a period outside of 1-15 range\n");
+
+    /* 16 is out of 1-15 range, so it should have no effect */
+    ok(NtSetTimerResolution(0, FALSE, &cur) == STATUS_TIMER_RESOLUTION_NOT_SET, "succeded to reset timer resolution even though it wasn't set\n");
+
+    ok(timeEndPeriod(3) == TIMERR_NOCANDO, "succeded to end period without matching begin\n");
+}
+
 static void test_timer(UINT period, UINT resolution)
 {
     MMRESULT rc;
@@ -197,6 +239,8 @@ START_TEST(timer)
 {
     test_timeGetDevCaps();
 
+    test_setting_resolution();
+
     if (tc.wPeriodMin <= 1) {
         test_timer(1, 0);
         test_timer(1, 1);
diff --git a/dlls/winmm/time.c b/dlls/winmm/time.c
index bdf4983c47..740ea7db58 100644
--- a/dlls/winmm/time.c
+++ b/dlls/winmm/time.c
@@ -23,9 +23,14 @@
 #include <stdarg.h>
 #include <errno.h>
 #include <time.h>
+#include <limits.h>
 
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#define NONAMELESSUNION
 #include "windef.h"
 #include "winbase.h"
+#include "winternl.h"
 #include "mmsystem.h"
 
 #include "winemm.h"
@@ -386,20 +391,44 @@ MMRESULT WINAPI timeGetDevCaps(LPTIMECAPS lpCaps, UINT wSize)
     return TIMERR_NOERROR;
 }
 
+extern NTSYSAPI NTSTATUS NTAPI NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
+
+/* Windows seem to allow to set each resolution betwen 1 and 15 exactly 65535 times before complaining*/
+static USHORT timer_request_count[15]; /* TODO: locking */
+
 /**************************************************************************
  * 				timeBeginPeriod		[WINMM.@]
  */
 MMRESULT WINAPI timeBeginPeriod(UINT wPeriod)
 {
-    if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL)
-	return TIMERR_NOCANDO;
+    NTSTATUS ret;
+    ULONG cur;
+    ULONG resolution = 0;
 
-    if (wPeriod > MMSYSTIME_MININTERVAL)
-    {
-        WARN("Stub; we set our timer resolution at minimum\n");
+    if (wPeriod == 0) /* illegal */
+        return TIMERR_NOCANDO;
+
+    if (wPeriod > 15) /* 16+ is more than the default 15.6ms - we don't even care */
+        return TIMERR_NOERROR;
+
+    if (timer_request_count[wPeriod] == USHRT_MAX)
+        return TIMERR_NOCANDO;
+
+    timer_request_count[wPeriod]++;
+
+    for (int i = 0; i < ARRAYSIZE(timer_request_count); i++) {
+        if (timer_request_count[i] != 0) {
+            resolution = i * 10000;
+            break;
+        }
     }
 
-    return 0;
+    ret = NtSetTimerResolution(resolution, TRUE, &cur);
+
+    if (ret == STATUS_SUCCESS)
+        return TIMERR_NOERROR;
+    else
+        return TIMERR_NOCANDO;
 }
 
 /**************************************************************************
@@ -407,12 +436,35 @@ MMRESULT WINAPI timeBeginPeriod(UINT wPeriod)
  */
 MMRESULT WINAPI timeEndPeriod(UINT wPeriod)
 {
-    if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL)
-	return TIMERR_NOCANDO;
+    ULONG cur;
+    ULONG resolution = 0;
+    NTSTATUS ret;
 
-    if (wPeriod > MMSYSTIME_MININTERVAL)
-    {
-        WARN("Stub; we set our timer resolution at minimum\n");
+    if (wPeriod == 0)
+        return TIMERR_NOCANDO;
+
+    if (wPeriod > 15)
+        return TIMERR_NOERROR;
+
+    if (timer_request_count[wPeriod] == 0)
+        return TIMERR_NOCANDO;
+
+    timer_request_count[wPeriod]--;
+
+    for (int i = 0; i < ARRAYSIZE(timer_request_count); i++) {
+        if (timer_request_count[i] != 0) {
+            resolution = i * 10000;
+            break;
+        }
     }
-    return 0;
+
+    if (resolution)
+        ret = NtSetTimerResolution(resolution, TRUE, &cur);
+    else
+        ret = NtSetTimerResolution(resolution, FALSE, &cur);
+
+    if (ret == STATUS_SUCCESS)
+        return TIMERR_NOERROR;
+    else
+        return TIMERR_NOCANDO;
 }
-- 
2.28.0




More information about the wine-devel mailing list