ntdll: Add a direct futex-based implementation of condition variables.

While the current path for condition variables will ultimately use futexes if
they are available, the path for address waits is vulnerable to several
spurious wakes, which can be obviated by using condition variables as futexes
directly.

This greatly improves performance for Path of Exile.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45524
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2019-02-10 12:26:45 -06:00 committed by Alexandre Julliard
parent 2d2e935190
commit be541f1b0c
1 changed files with 67 additions and 17 deletions

View File

@ -113,6 +113,23 @@ static inline int use_futexes(void)
}
return supported;
}
static void timespec_from_timeout( struct timespec *timespec, const LARGE_INTEGER *timeout )
{
LARGE_INTEGER now;
timeout_t diff;
if (timeout->QuadPart > 0)
{
NtQuerySystemTime( &now );
diff = timeout->QuadPart - now.QuadPart;
}
else
diff = -timeout->QuadPart;
timespec->tv_sec = diff / TICKSPERSEC;
timespec->tv_nsec = (diff % TICKSPERSEC) * 100;
}
#endif
/* creates a struct security_descriptor and contained information in one contiguous piece of memory */
@ -1876,6 +1893,47 @@ BOOLEAN WINAPI RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock )
return TRUE;
}
#ifdef __linux__
static NTSTATUS fast_wait_cv( RTL_CONDITION_VARIABLE *variable, int val, const LARGE_INTEGER *timeout )
{
struct timespec timespec;
int ret;
if (!use_futexes())
return STATUS_NOT_IMPLEMENTED;
if (timeout && timeout->QuadPart != TIMEOUT_INFINITE)
{
timespec_from_timeout( &timespec, timeout );
ret = futex_wait( (int *)&variable->Ptr, val, &timespec );
}
else
ret = futex_wait( (int *)&variable->Ptr, val, NULL );
if (ret == -1 && errno == ETIMEDOUT)
return STATUS_TIMEOUT;
return STATUS_WAIT_0;
}
static NTSTATUS fast_wake_cv( RTL_CONDITION_VARIABLE *variable, int count )
{
if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
futex_wake( (int *)&variable->Ptr, count );
return STATUS_SUCCESS;
}
#else
static NTSTATUS fast_wait_cv( RTL_CONDITION_VARIABLE *variable, int val, const LARGE_INTEGER *timeout )
{
return STATUS_NOT_IMPLEMENTED;
}
static NTSTATUS fast_wake_cv( RTL_CONDITION_VARIABLE *variable, int count )
{
return STATUS_NOT_IMPLEMENTED;
}
#endif
/***********************************************************************
* RtlInitializeConditionVariable (NTDLL.@)
*
@ -1910,7 +1968,8 @@ void WINAPI RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE *variable )
void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable )
{
interlocked_xchg_add( (int *)&variable->Ptr, 1 );
RtlWakeAddressSingle( variable );
if (fast_wake_cv( variable, 1 ) == STATUS_NOT_IMPLEMENTED)
RtlWakeAddressSingle( variable );
}
/***********************************************************************
@ -1921,7 +1980,8 @@ void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable )
void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable )
{
interlocked_xchg_add( (int *)&variable->Ptr, 1 );
RtlWakeAddressAll( variable );
if (fast_wake_cv( variable, INT_MAX ) == STATUS_NOT_IMPLEMENTED)
RtlWakeAddressAll( variable );
}
/***********************************************************************
@ -1947,7 +2007,8 @@ NTSTATUS WINAPI RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE *variable, R
RtlLeaveCriticalSection( crit );
status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
if ((status = fast_wait_cv( variable, val, timeout )) == STATUS_NOT_IMPLEMENTED)
status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
RtlEnterCriticalSection( crit );
@ -1984,7 +2045,8 @@ NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable,
else
RtlReleaseSRWLockExclusive( lock );
status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
if ((status = fast_wait_cv( variable, val, timeout )) == STATUS_NOT_IMPLEMENTED)
status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
RtlAcquireSRWLockShared( lock );
@ -2039,8 +2101,6 @@ static inline NTSTATUS fast_wait_addr( const void *addr, const void *cmp, SIZE_T
{
int *futex;
int val;
LARGE_INTEGER now;
timeout_t diff;
struct timespec timespec;
int ret;
@ -2060,17 +2120,7 @@ static inline NTSTATUS fast_wait_addr( const void *addr, const void *cmp, SIZE_T
if (timeout)
{
if (timeout->QuadPart > 0)
{
NtQuerySystemTime( &now );
diff = timeout->QuadPart - now.QuadPart;
}
else
diff = -timeout->QuadPart;
timespec.tv_sec = diff / TICKSPERSEC;
timespec.tv_nsec = (diff % TICKSPERSEC) * 100;
timespec_from_timeout( &timespec, timeout );
ret = futex_wait( futex, val, &timespec );
}
else