On top of NtSetTimerResolution()...
missing: thread safety
Signed-off-by: Arkadiusz Hiler ahiler@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; }