Erich Hoover : user32: SetTimer and SetSystemTimer should respect the timeout limits.

Alexandre Julliard julliard at winehq.org
Mon May 6 14:08:45 CDT 2013


Module: wine
Branch: master
Commit: 057b0d8bcac633ff80238c6de3ece04655eccf2c
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=057b0d8bcac633ff80238c6de3ece04655eccf2c

Author: Erich Hoover <ehoover at mines.edu>
Date:   Thu May  2 21:18:03 2013 -0600

user32: SetTimer and SetSystemTimer should respect the timeout limits.

---

 dlls/user32/message.c   |   14 +++++--
 dlls/user32/tests/msg.c |   84 ++++++++++++++++++++++++++++++++++++++++-------
 include/winuser.h       |    4 ++
 3 files changed, 86 insertions(+), 16 deletions(-)

diff --git a/dlls/user32/message.c b/dlls/user32/message.c
index 09a4cb3..e819983 100644
--- a/dlls/user32/message.c
+++ b/dlls/user32/message.c
@@ -56,8 +56,6 @@ WINE_DECLARE_DEBUG_CHANNEL(key);
 
 #define MAX_PACK_COUNT 4
 
-#define SYS_TIMER_RATE  55   /* min. timer rate in ms (actually 54.925)*/
-
 /* the various structures that can be sent in messages, in platform-independent layout */
 struct packed_CREATESTRUCTW
 {
@@ -4385,12 +4383,16 @@ UINT_PTR WINAPI SetTimer( HWND hwnd, UINT_PTR id, UINT timeout, TIMERPROC proc )
 
     if (proc) winproc = WINPROC_AllocProc( (WNDPROC)proc, FALSE );
 
+    /* MSDN states that the minimum timeout should be USER_TIMER_MINIMUM (10.0 ms), but testing
+     * indicates that the true minimum is closer to 15.6 ms. */
+    timeout = min( max( 15, timeout ), USER_TIMER_MAXIMUM );
+
     SERVER_START_REQ( set_win_timer )
     {
         req->win    = wine_server_user_handle( hwnd );
         req->msg    = WM_TIMER;
         req->id     = id;
-        req->rate   = max( timeout, SYS_TIMER_RATE );
+        req->rate   = timeout;
         req->lparam = (ULONG_PTR)winproc;
         if (!wine_server_call_err( req ))
         {
@@ -4416,12 +4418,16 @@ UINT_PTR WINAPI SetSystemTimer( HWND hwnd, UINT_PTR id, UINT timeout, TIMERPROC
 
     if (proc) winproc = WINPROC_AllocProc( (WNDPROC)proc, FALSE );
 
+    /* MSDN states that the minimum timeout should be USER_TIMER_MINIMUM (10.0 ms), but testing
+     * indicates that the true minimum is closer to 15.6 ms. */
+    timeout = min( max( 15, timeout ), USER_TIMER_MAXIMUM );
+
     SERVER_START_REQ( set_win_timer )
     {
         req->win    = wine_server_user_handle( hwnd );
         req->msg    = WM_SYSTIMER;
         req->id     = id;
-        req->rate   = max( timeout, SYS_TIMER_RATE );
+        req->rate   = timeout;
         req->lparam = (ULONG_PTR)winproc;
         if (!wine_server_call_err( req ))
         {
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c
index ac3ba16..d88dc7e 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -1722,6 +1722,8 @@ static BOOL (WINAPI *pUnhookWinEvent)(HWINEVENTHOOK);
 static BOOL (WINAPI *pGetMonitorInfoA)(HMONITOR,LPMONITORINFO);
 static HMONITOR (WINAPI *pMonitorFromPoint)(POINT,DWORD);
 static BOOL (WINAPI *pUpdateLayeredWindow)(HWND,HDC,POINT*,SIZE*,HDC,POINT*,COLORREF,BLENDFUNCTION*,DWORD);
+static UINT_PTR (WINAPI *pSetSystemTimer)(HWND, UINT_PTR, UINT, TIMERPROC);
+static UINT_PTR (WINAPI *pKillSystemTimer)(HWND, UINT_PTR);
 /* kernel32 functions */
 static BOOL (WINAPI *pGetCPInfoExA)(UINT, DWORD, LPCPINFOEXA);
 
@@ -1746,6 +1748,8 @@ static void init_procs(void)
     GET_PROC(user32, GetMonitorInfoA)
     GET_PROC(user32, MonitorFromPoint)
     GET_PROC(user32, UpdateLayeredWindow)
+    GET_PROC(user32, SetSystemTimer)
+    GET_PROC(user32, KillSystemTimer)
 
     GET_PROC(kernel32, GetCPInfoExA)
 
@@ -8156,7 +8160,15 @@ static VOID CALLBACK tfunc(HWND hwnd, UINT uMsg, UINT_PTR id, DWORD dwTime)
 {
 }
 
-#define TIMER_ID  0x19
+#define TIMER_ID               0x19
+#define TIMER_COUNT_EXPECTED   64
+#define TIMER_COUNT_TOLERANCE  9
+
+static int count = 0;
+static void CALLBACK callback_count(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+    count++;
+}
 
 static DWORD WINAPI timer_thread_proc(LPVOID x)
 {
@@ -8176,7 +8188,9 @@ static DWORD WINAPI timer_thread_proc(LPVOID x)
 static void test_timers(void)
 {
     struct timer_info info;
+    DWORD start;
     DWORD id;
+    MSG msg;
 
     info.hWnd = CreateWindow ("TestWindowClass", NULL,
        WS_OVERLAPPEDWINDOW ,
@@ -8198,23 +8212,53 @@ static void test_timers(void)
 
     ok( KillTimer(info.hWnd, TIMER_ID), "KillTimer failed\n");
 
-    ok(DestroyWindow(info.hWnd), "failed to destroy window\n");
-}
+    /* Check the minimum allowed timeout for a timer.  MSDN indicates that it should be 10.0 ms,
+     * but testing indicates that the minimum timeout is actually about 15.6 ms.  Since there is
+     * some measurement error between test runs we're allowing for ±8 counts (~2 ms).
+     */
+    count = 0;
+    id = SetTimer(info.hWnd, TIMER_ID, 0, callback_count);
+    ok(id != 0, "did not get id from SetTimer.\n");
+    ok(id==TIMER_ID, "SetTimer timer ID different\n");
+    start = GetTickCount();
+    while (GetTickCount()-start < 1001 && GetMessage(&msg, info.hWnd, 0, 0))
+        DispatchMessage(&msg);
+    ok(abs(count-TIMER_COUNT_EXPECTED) < TIMER_COUNT_TOLERANCE
+       || broken(abs(count-43) < TIMER_COUNT_TOLERANCE) /* w2k3 */,
+       "did not get expected count for minimum timeout (%d != ~%d).\n",
+       count, TIMER_COUNT_EXPECTED);
+    ok(KillTimer(info.hWnd, id), "KillTimer failed\n");
+    /* Perform the same check on SetSystemTimer (only available on w2k3 and older) */
+    if (pSetSystemTimer)
+    {
+        int syscount = 0;
+
+        count = 0;
+        id = pSetSystemTimer(info.hWnd, TIMER_ID, 0, callback_count);
+        ok(id != 0, "did not get id from SetSystemTimer.\n");
+        ok(id==TIMER_ID, "SetTimer timer ID different\n");
+        start = GetTickCount();
+        while (GetTickCount()-start < 1001 && GetMessage(&msg, info.hWnd, 0, 0))
+        {
+            if (msg.message == WM_SYSTIMER)
+                syscount++;
+            DispatchMessage(&msg);
+        }
+        ok(abs(syscount-TIMER_COUNT_EXPECTED) < TIMER_COUNT_TOLERANCE,
+           "did not get expected count for minimum timeout (%d != ~%d).\n",
+           syscount, TIMER_COUNT_EXPECTED);
+        todo_wine ok(count == 0, "did not get expected count for callback timeout (%d != 0).\n",
+                                 count);
+        ok(pKillSystemTimer(info.hWnd, id), "KillSystemTimer failed\n");
+    }
 
-static int count = 0;
-static VOID CALLBACK callback_count(
-    HWND hwnd,
-    UINT uMsg,
-    UINT_PTR idEvent,
-    DWORD dwTime
-)
-{
-    count++;
+    ok(DestroyWindow(info.hWnd), "failed to destroy window\n");
 }
 
 static void test_timers_no_wnd(void)
 {
     UINT_PTR id, id2;
+    DWORD start;
     MSG msg;
 
     count = 0;
@@ -8232,6 +8276,22 @@ static void test_timers_no_wnd(void)
     Sleep(250);
     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
     ok(count == 1, "killing replaced timer did not work (%i).\n", count);
+
+    /* Check the minimum allowed timeout for a timer.  MSDN indicates that it should be 10.0 ms,
+     * but testing indicates that the minimum timeout is actually about 15.6 ms.  Since there is
+     * some measurement error between test runs we're allowing for ±8 counts (~2 ms).
+     */
+    count = 0;
+    id = SetTimer(NULL, 0, 0, callback_count);
+    ok(id != 0, "did not get id from SetTimer.\n");
+    start = GetTickCount();
+    while (GetTickCount()-start < 1001 && GetMessage(&msg, NULL, 0, 0))
+        DispatchMessage(&msg);
+    ok(abs(count-TIMER_COUNT_EXPECTED) < TIMER_COUNT_TOLERANCE,
+       "did not get expected count for minimum timeout (%d != ~%d).\n",
+       count, TIMER_COUNT_EXPECTED);
+    KillTimer(NULL, id);
+    /* Note: SetSystemTimer doesn't support a NULL window, see test_timers */
 }
 
 /* Various win events with arbitrary parameters */
diff --git a/include/winuser.h b/include/winuser.h
index 59d5b8e..fb0fa95 100644
--- a/include/winuser.h
+++ b/include/winuser.h
@@ -2521,6 +2521,10 @@ typedef struct tagMINIMIZEDMETRICS {
 #define PM_QS_PAINT       (QS_PAINT << 16)
 #define PM_QS_SENDMESSAGE (QS_SENDMESSAGE << 16)
 
+/* SetTimer() limits */
+#define USER_TIMER_MINIMUM 0x0000000A
+#define USER_TIMER_MAXIMUM 0x7FFFFFFF
+
 /* AnimateWindow() flags */
 #define AW_SLIDE        0x00040000
 #define AW_ACTIVATE     0x00020000




More information about the wine-cvs mailing list