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

Zebediah Figura z.figura12 at gmail.com
Sun Feb 10 12:26:45 CST 2019


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 at gmail.com>
---
 dlls/ntdll/sync.c | 84 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 67 insertions(+), 17 deletions(-)

diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 693013f5a3..3293c52859 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -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
-- 
2.20.1




More information about the wine-devel mailing list