[PATCH 2/5] ntdll: Make wait/delay calls respect the timer resolution
Arkadiusz Hiler
ahiler at codeweavers.com
Mon Aug 17 12:46:51 CDT 2020
NtDelayExecution() and all the calls using server_wait() are now rounding up
their wait times to the next multiply of set timer resolution.
Affected calls are:
* NtDelayExecution()
* NtWaitForSingleObject()
* NtWaitForMultipleObjects()
* NtSignalAndWaitForSingleObject()
* NtCreateKeyedEvent()
* NtWaitForKeyedEvent()
* NtReleaseKeyedEvent()
Signed-off-by: Arkadiusz Hiler <ahiler at codeweavers.com>
---
dlls/ntdll/tests/time.c | 153 +++++++++++++++++++++++++++++++++
dlls/ntdll/unix/server.c | 16 +++-
dlls/ntdll/unix/sync.c | 29 +++++--
dlls/ntdll/unix/unix_private.h | 10 +++
4 files changed, 201 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c
index 6c800c8c4e..0f4096f708 100644
--- a/dlls/ntdll/tests/time.c
+++ b/dlls/ntdll/tests/time.c
@@ -37,6 +37,13 @@ static BOOL (WINAPI *pRtlQueryUnbiasedInterruptTime)( ULONGLONG *time );
static NTSTATUS (WINAPI *pNtQueryTimerResolution)( ULONG *min_res, ULONG *max_res, ULONG *current_res );
static NTSTATUS (WINAPI *pNtSetTimerResolution)( ULONG res, BOOLEAN set, ULONG *current_res );
+static NTSTATUS (WINAPI *pNtWaitForSingleObject)(HANDLE, BOOLEAN, const LARGE_INTEGER *);
+static NTSTATUS (WINAPI *pNtWaitForMultipleObjects)(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,const LARGE_INTEGER*);
+static NTSTATUS (WINAPI *pNtSignalAndWaitForSingleObject)( HANDLE signal, HANDLE wait,
+ BOOLEAN alertable, const LARGE_INTEGER *timeout );
+static NTSTATUS (WINAPI *pNtCreateKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, ULONG );
+static NTSTATUS (WINAPI *pNtWaitForKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * );
+static NTSTATUS (WINAPI *pNtReleaseKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * );
static const int MonthLengths[2][12] =
{
@@ -267,6 +274,46 @@ static void test_user_shared_data_time(void)
todo_wine ok(changed >= 90, "tick count isn't updated after sleeping one millisecond (%d%% correct)\n", changed);
}
+BOOL spin_the_thread = TRUE;
+
+DWORD WINAPI spinner_thread(void *arg)
+{
+ while (spin_the_thread)
+ Sleep(1);
+
+ return 0;
+}
+
+#define OBEYS_RESOLUTION(call) \
+ for (ULONGLONG _resolution = 30000; _resolution <= 90000; _resolution += 30000) \
+ { \
+ ULONG _cur; \
+ LARGE_INTEGER _counter[21]; \
+ int _hits = 0; \
+ pNtSetTimerResolution(_resolution, TRUE, &_cur); \
+ \
+ QueryPerformanceCounter(&_counter[0]); \
+ for (int _i = 1; _i < ARRAYSIZE(_counter); _i++) \
+ { \
+ (call); \
+ QueryPerformanceCounter(&_counter[_i]); \
+ } \
+ \
+ for (int _i = 1; _i < ARRAYSIZE(_counter); _i++) \
+ { \
+ ULONGLONG _delta = _counter[_i].QuadPart - _counter[_i-1].QuadPart; \
+ if (((_cur-20000) < _delta) && ((_cur+20000) > _delta)) \
+ _hits++; \
+ } \
+ \
+ pNtSetTimerResolution(_resolution, FALSE, &_cur); \
+ \
+ ok(_hits >= ((8*ARRAYSIZE(_counter)-1)/10), \
+ #call " hit the expected interval only %u times out of %u for res %u\n", \
+ _hits, ARRAYSIZE(_counter)-1, _resolution); \
+ }
+
+
static void test_timer_resolution(void)
{
ULONG min, max, cur, def, cur2;
@@ -291,6 +338,105 @@ static void test_timer_resolution(void)
ok(cur == def, "after unsetting we've got %u instead of the default %u\n", cur, def);
ok(pNtSetTimerResolution(123456, FALSE, &cur) == STATUS_TIMER_RESOLUTION_NOT_SET, "we have unset resolution more times than one\n");
+
+ {
+ LARGE_INTEGER timeout;
+
+ timeout.QuadPart = -10000;
+ OBEYS_RESOLUTION(NtDelayExecution(TRUE, &timeout));
+ OBEYS_RESOLUTION(NtDelayExecution(FALSE, &timeout));
+
+ OBEYS_RESOLUTION(
+ {
+ NtQuerySystemTime(&timeout);
+ timeout.QuadPart += 10000;
+ NtDelayExecution(TRUE, &timeout);
+ });
+
+ OBEYS_RESOLUTION(
+ {
+ NtQuerySystemTime(&timeout);
+ timeout.QuadPart += 10000;
+ NtDelayExecution(FALSE, &timeout);
+ });
+ }
+
+ {
+ DWORD thread_id;
+ LARGE_INTEGER timeout;
+ HANDLE thread = CreateThread( NULL, 0, spinner_thread, NULL, 0, &thread_id );
+ HANDLE event = CreateEventA( NULL, TRUE, FALSE, NULL );
+
+ timeout.QuadPart = -10000;
+ OBEYS_RESOLUTION(pNtWaitForSingleObject( thread, TRUE, &timeout ));
+ OBEYS_RESOLUTION(pNtWaitForSingleObject( thread, FALSE, &timeout ));
+
+ OBEYS_RESOLUTION(
+ {
+ NtQuerySystemTime(&timeout);
+ timeout.QuadPart += 10000;
+ pNtWaitForSingleObject( thread, TRUE, &timeout );
+ });
+
+ OBEYS_RESOLUTION(
+ {
+ NtQuerySystemTime(&timeout);
+ timeout.QuadPart += 10000;
+ pNtWaitForSingleObject( thread, FALSE, &timeout );
+ });
+
+ /* XXX: saving some test time here on just the relative alertable variants */
+ timeout.QuadPart = -10000;
+ OBEYS_RESOLUTION(pNtWaitForMultipleObjects( 1, &thread, TRUE, TRUE, &timeout ));
+ OBEYS_RESOLUTION({
+ pNtSignalAndWaitForSingleObject( event, thread, TRUE, &timeout );
+ ResetEvent( event );
+ });
+
+ spin_the_thread = FALSE;
+ WaitForSingleObject( thread, 0 );
+ }
+
+ {
+ HANDLE event;
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = -10000;
+
+ ok(pNtCreateKeyedEvent( &event, GENERIC_ALL, NULL, 0 ) == STATUS_SUCCESS, "failed to create keyed event\n");
+ OBEYS_RESOLUTION(pNtWaitForKeyedEvent( event, (void *) 2, FALSE, &timeout ));
+ OBEYS_RESOLUTION(pNtWaitForKeyedEvent( event, (void *) 2, TRUE, &timeout ));
+ OBEYS_RESOLUTION(pNtReleaseKeyedEvent( event, (void *) 2, FALSE, &timeout ));
+ OBEYS_RESOLUTION(pNtReleaseKeyedEvent( event, (void *) 2, TRUE, &timeout ));
+
+ OBEYS_RESOLUTION(
+ {
+ NtQuerySystemTime( &timeout );
+ timeout.QuadPart += 10000;
+ pNtWaitForKeyedEvent( event, (void *) 2, FALSE, &timeout );
+ });
+
+ OBEYS_RESOLUTION(
+ {
+ NtQuerySystemTime( &timeout );
+ timeout.QuadPart += 10000;
+ pNtWaitForKeyedEvent( event, (void *) 2, TRUE, &timeout );
+ });
+
+ OBEYS_RESOLUTION(
+ {
+ NtQuerySystemTime( &timeout );
+ timeout.QuadPart += 10000;
+ pNtReleaseKeyedEvent( event, (void *) 2, FALSE, &timeout );
+ });
+
+ OBEYS_RESOLUTION(
+ {
+ NtQuerySystemTime( &timeout );
+ timeout.QuadPart += 10000;
+ pNtReleaseKeyedEvent( event, (void *) 2, TRUE, &timeout );
+ });
+ }
+
}
START_TEST(time)
@@ -305,8 +451,15 @@ START_TEST(time)
pRtlQueryDynamicTimeZoneInformation =
(void *)GetProcAddress(mod, "RtlQueryDynamicTimeZoneInformation");
pRtlQueryUnbiasedInterruptTime = (void *)GetProcAddress(mod, "RtlQueryUnbiasedInterruptTime");
+
pNtQueryTimerResolution = (void *)GetProcAddress(mod, "NtQueryTimerResolution");
pNtSetTimerResolution = (void *)GetProcAddress(mod, "NtSetTimerResolution");
+ pNtWaitForSingleObject = (void *)GetProcAddress(mod, "NtWaitForSingleObject");
+ pNtWaitForMultipleObjects = (void *)GetProcAddress(mod, "NtWaitForMultipleObjects");
+ pNtSignalAndWaitForSingleObject = (void *)GetProcAddress(mod, "NtSignalAndWaitForSingleObject");
+ pNtCreateKeyedEvent = (void *)GetProcAddress(mod, "NtCreateKeyedEvent");
+ pNtWaitForKeyedEvent = (void *)GetProcAddress(mod, "NtWaitForKeyedEvent");
+ pNtReleaseKeyedEvent = (void *)GetProcAddress(mod, "NtReleaseKeyedEvent");
if (pRtlTimeToTimeFields && pRtlTimeFieldsToTime)
test_pRtlTimeToTimeFields();
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c
index d867a7fb46..17d125bbf6 100644
--- a/dlls/ntdll/unix/server.c
+++ b/dlls/ntdll/unix/server.c
@@ -672,11 +672,23 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f
user_apc_t apc;
if (abs_timeout < 0)
+ {
+ LARGE_INTEGER since_server_start;
+
+ NtQueryPerformanceCounter( &since_server_start, NULL );
+ abs_timeout = -multiple_of_time_resolution( -abs_timeout );
+ abs_timeout -= since_server_start.QuadPart;
+ }
+ else
{
LARGE_INTEGER now;
- NtQueryPerformanceCounter( &now, NULL );
- abs_timeout -= now.QuadPart;
+ NtQuerySystemTime( &now );
+ if (abs_timeout > now.QuadPart)
+ {
+ abs_timeout = multiple_of_time_resolution( abs_timeout - now.QuadPart );
+ abs_timeout += now.QuadPart;
+ }
}
ret = server_select( select_op, size, flags, abs_timeout, NULL, NULL, &apc );
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index eb93ac0d4b..8d4cc580eb 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -85,7 +85,7 @@ static ULONG timer_resolution_current = 0;
static const ULONG timer_resolution_min = 156250; /* also the default */
static const ULONG timer_resolution_max = 5000;
-static ULONG get_timer_resolution(void)
+ULONG get_timer_resolution(void)
{
return timer_resolution_current ? timer_resolution_current : timer_resolution_min;
}
@@ -1341,10 +1341,17 @@ NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeou
LARGE_INTEGER now;
timeout_t when, diff;
+ NtQuerySystemTime( &now );
+
if ((when = timeout->QuadPart) < 0)
{
- NtQuerySystemTime( &now );
- when = now.QuadPart - when;
+ when = multiple_of_time_resolution( -when );
+ when += now.QuadPart;
+ }
+ else if (when > now.QuadPart)
+ {
+ when = multiple_of_time_resolution( when - now.QuadPart );
+ when += now.QuadPart;
}
/* Note that we yield after establishing the desired timeout */
@@ -2782,11 +2789,23 @@ NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size
}
if (abs_timeout < 0)
+ {
+ LARGE_INTEGER since_server_start;
+
+ NtQueryPerformanceCounter( &since_server_start, NULL );
+ abs_timeout = -multiple_of_time_resolution( -abs_timeout );
+ abs_timeout -= since_server_start.QuadPart;
+ }
+ else
{
LARGE_INTEGER now;
- NtQueryPerformanceCounter( &now, NULL );
- abs_timeout -= now.QuadPart;
+ NtQuerySystemTime( &now );
+ if (abs_timeout > now.QuadPart)
+ {
+ abs_timeout = multiple_of_time_resolution( abs_timeout - now.QuadPart );
+ abs_timeout += now.QuadPart;
+ }
}
select_op.keyed_event.op = SELECT_KEYED_EVENT_WAIT;
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index 397211957b..5b23c4e2aa 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -264,6 +264,10 @@ extern void WINAPI DECLSPEC_NORETURN call_user_exception_dispatcher( EXCEPTION_R
NTSTATUS (WINAPI *dispatcher)(EXCEPTION_RECORD*,CONTEXT*) ) DECLSPEC_HIDDEN;
extern void WINAPI DECLSPEC_NORETURN call_raise_user_exception_dispatcher( NTSTATUS (WINAPI *dispatcher)(void) ) DECLSPEC_HIDDEN;
+extern ULONG get_timer_resolution(void) DECLSPEC_HIDDEN;
+
+
+
#define TICKSPERSEC 10000000
#define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)86400)
#define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
@@ -291,6 +295,12 @@ static inline void *get_signal_stack(void)
return (char *)NtCurrentTeb() + teb_size - teb_offset;
}
+static inline timeout_t multiple_of_time_resolution(timeout_t timeout)
+{
+ ULONG res = get_timer_resolution();
+ return ((timeout + res - 1) / res) * res;
+}
+
#ifndef _WIN64
static inline TEB64 *NtCurrentTeb64(void) { return (TEB64 *)NtCurrentTeb()->GdiBatchCount; }
#endif
--
2.28.0
More information about the wine-devel
mailing list