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@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