[PATCH] kernelbase: Fix relationship between Sleep() and GetTickCount()

Arkadiusz Hiler ahiler at codeweavers.com
Mon Aug 10 05:55:45 CDT 2020


Sleep() and GetTickCount() work on Windows in 15.6ms increments.

Some programs (DOSBox) are depending on this behavior and are assuming that
the return value of GetTickCount() will change during sleeping.

Currently we are updating shared counters used by GetTickCount() every 16ms +
on each server request, and our Sleep() implementation has resolution of 1ms,
which causes DOSBox to hang.

This patch changes Sleep() (and SleepEx()) to behave the same way as on
Windows and makes sure that GetTickCount() will be updated during sleeping by
decreasing the update interval to 15ms (worst case, without any server calls).

This fixes Doom II from Steam.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49564
Signed-off-by: Arkadiusz Hiler <ahiler at codeweavers.com>
---
 dlls/kernelbase/sync.c  | 20 ++++++++++++++++----
 dlls/ntdll/tests/time.c |  2 +-
 server/fd.c             |  2 +-
 3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c
index 13a9938e7c..673c5b60d9 100644
--- a/dlls/kernelbase/sync.c
+++ b/dlls/kernelbase/sync.c
@@ -51,13 +51,25 @@ static inline BOOL is_version_nt(void)
 }
 
 /* helper for kernel32->ntdll timeout format conversion */
-static inline LARGE_INTEGER *get_nt_timeout( LARGE_INTEGER *time, DWORD timeout )
+static inline LARGE_INTEGER *get_aligned_nt_timeout( LARGE_INTEGER *time, DWORD timeout, DWORD alignment )
 {
+    ULONGLONG quad ;
+
     if (timeout == INFINITE) return NULL;
-    time->QuadPart = (ULONGLONG)timeout * -10000;
+
+    quad = (ULONGLONG)timeout * 10000;
+    quad = ((quad + alignment - 1) / alignment) * alignment;
+
+    time->QuadPart = quad * -1;
+
     return time;
 }
 
+static inline LARGE_INTEGER *get_nt_timeout( LARGE_INTEGER *time, DWORD timeout)
+{
+    return get_aligned_nt_timeout(time, timeout, 10000);
+}
+
 
 /***********************************************************************
  *              BaseGetNamedObjectDirectory  (kernelbase.@)
@@ -269,7 +281,7 @@ void WINAPI DECLSPEC_HOTPATCH Sleep( DWORD timeout )
 {
     LARGE_INTEGER time;
 
-    NtDelayExecution( FALSE, get_nt_timeout( &time, timeout ) );
+    NtDelayExecution( FALSE, get_aligned_nt_timeout( &time, timeout, 156000 ) );
 }
 
 
@@ -281,7 +293,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH SleepEx( DWORD timeout, BOOL alertable )
     NTSTATUS status;
     LARGE_INTEGER time;
 
-    status = NtDelayExecution( alertable, get_nt_timeout( &time, timeout ) );
+    status = NtDelayExecution( alertable, get_aligned_nt_timeout( &time, timeout, 156000 ) );
     if (status == STATUS_USER_APC) return WAIT_IO_COMPLETION;
     return 0;
 }
diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c
index a00d507e4e..0e0efe6384 100644
--- a/dlls/ntdll/tests/time.c
+++ b/dlls/ntdll/tests/time.c
@@ -261,7 +261,7 @@ static void test_user_shared_data_time(void)
         t2 = GetTickCount();
         if (t1 != t2) changed++;
     }
-    todo_wine ok(changed >= 90, "tick count isn't updated after sleeping one millisecond (%d%% correct)\n", changed);
+    ok(changed >= 90, "tick count isn't updated after sleeping one millisecond (%d%% correct)\n", changed);
 }
 
 START_TEST(time)
diff --git a/server/fd.c b/server/fd.c
index 7ea8ac273e..1bdce6c70c 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -377,7 +377,7 @@ timeout_t current_time;
 timeout_t monotonic_time;
 
 struct _KUSER_SHARED_DATA *user_shared_data = NULL;
-static const int user_shared_data_timeout = 16;
+static const int user_shared_data_timeout = 15;
 
 static void set_user_shared_data_time(void)
 {
-- 
2.28.0




More information about the wine-devel mailing list