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

Alexandre Julliard julliard at winehq.org
Mon Feb 11 16:12:10 CST 2019


Module: wine
Branch: master
Commit: be541f1b0cf2bec93b59d3a2fde2b6c0593c011b
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=be541f1b0cf2bec93b59d3a2fde2b6c0593c011b

Author: Zebediah Figura <z.figura12 at gmail.com>
Date:   Sun Feb 10 12:26:45 2019 -0600

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 at gmail.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 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 693013f..3293c52 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




More information about the wine-cvs mailing list