[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