[Bug 46252] StarCitizen has bad performance

wine-bugs at winehq.org wine-bugs at winehq.org
Wed Jan 23 13:21:49 CST 2019


https://bugs.winehq.org/show_bug.cgi?id=46252

--- Comment #10 from rawfox <rawfox at freenet.de> ---
Comment on attachment 63216
  --> https://bugs.winehq.org/attachment.cgi?id=63216
merged futex conditions patch for wine vanilla

>--- a/dlls/ntdll/sync.c	2019-01-07 22:06:29.876808084 +0100
>+++ b/dlls/ntdll/sync.c	2019-01-07 23:51:22.897154211 +0100
>@@ -26,6 +26,7 @@
> 
> #include <assert.h>
> #include <errno.h>
>+#include <limits.h>
> #include <signal.h>
> #ifdef HAVE_SYS_TIME_H
> # include <sys/time.h>
>@@ -36,6 +37,9 @@
> #ifdef HAVE_SYS_POLL_H
> # include <sys/poll.h>
> #endif
>+#ifdef HAVE_SYS_SYSCALL_H
>+# include <sys/syscall.h>
>+#endif
> #ifdef HAVE_UNISTD_H
> # include <unistd.h>
> #endif
>@@ -61,7 +65,140 @@
> 
> HANDLE keyed_event = NULL;
> 
>+
> static const LARGE_INTEGER zero_timeout;
>+#define TICKSPERSEC        10000000
>+
>+#ifdef __linux__
>+
>+static int wait_op = 128; /*FUTEX_WAIT|FUTEX_PRIVATE_FLAG*/
>+static int wake_op = 129; /*FUTEX_WAKE|FUTEX_PRIVATE_FLAG*/
>+
>+static inline int futex_wait( int *addr, int val, struct timespec *timeout )
>+{
>+    return syscall( __NR_futex, addr, wait_op, val, timeout, 0, 0 );
>+}
>+
>+static inline int futex_wake( int *addr, int val )
>+{
>+    return syscall( __NR_futex, addr, wake_op, val, NULL, 0, 0 );
>+}
>+
>+static inline int use_futexes(void)
>+{
>+    static int supported = -1;
>+
>+    if (supported == -1)
>+    {
>+        futex_wait( &supported, 10, NULL );
>+        if (errno == ENOSYS)
>+        {
>+            wait_op = 0; /*FUTEX_WAIT*/
>+            wake_op = 1; /*FUTEX_WAKE*/
>+            futex_wait( &supported, 10, NULL );
>+        }
>+        supported = (errno != ENOSYS);
>+    }
>+    return supported;
>+}
>+
>+static inline NTSTATUS fast_wait( RTL_CONDITION_VARIABLE *variable, int val,
>+    const LARGE_INTEGER *timeout)
>+{
>+    struct timespec timespec;
>+    LONGLONG timeleft;
>+    LARGE_INTEGER now;
>+    int ret;
>+
>+    if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
>+
>+    if (timeout && timeout->QuadPart != TIMEOUT_INFINITE)
>+    {
>+        if (timeout->QuadPart >= 0)
>+        {
>+            NtQuerySystemTime( &now );
>+            timeleft = timeout->QuadPart - now.QuadPart;
>+            if (timeleft < 0) timeleft = 0;
>+        }
>+        else
>+            timeleft = -timeout->QuadPart;
>+
>+        timespec.tv_sec = timeleft / TICKSPERSEC;
>+        timespec.tv_nsec = (timeleft % TICKSPERSEC) * 100;
>+
>+        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 inline NTSTATUS fast_sleep_cs( RTL_CONDITION_VARIABLE *variable,
>+    RTL_CRITICAL_SECTION *crit, const LARGE_INTEGER *timeout )
>+{
>+    int val = *(int *)&variable->Ptr;
>+    NTSTATUS ret;
>+
>+    RtlLeaveCriticalSection( crit );
>+
>+    ret = fast_wait( variable, val, timeout );
>+
>+    RtlEnterCriticalSection( crit );
>+
>+    return ret;
>+}
>+
>+static inline NTSTATUS fast_sleep_srw( RTL_CONDITION_VARIABLE *variable,
>+    RTL_SRWLOCK *lock, const LARGE_INTEGER *timeout, ULONG flags )
>+{
>+    int val = *(int *)&variable->Ptr;
>+    NTSTATUS ret;
>+
>+    if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
>+        RtlReleaseSRWLockShared( lock );
>+    else
>+        RtlReleaseSRWLockExclusive( lock );
>+
>+    ret = fast_wait( variable, val, timeout );
>+
>+    if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
>+        RtlAcquireSRWLockShared( lock );
>+    else
>+        RtlAcquireSRWLockExclusive( lock );
>+
>+    return ret;
>+}
>+
>+static inline NTSTATUS fast_wake( RTL_CONDITION_VARIABLE *variable, int val )
>+{
>+    if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
>+
>+    interlocked_xchg_add( (int *)&variable->Ptr, 1 );
>+    futex_wake( (int *)&variable->Ptr, val );
>+    return STATUS_SUCCESS;
>+}
>+
>+#else
>+static inline NTSTATUS fast_sleep_cs( RTL_CONDITION_VARIABLE *variable,
>+    RTL_CRITICAL_SECTION *crit, const LARGE_INTEGER *timeout )
>+{
>+    return STATUS_NOT_IMPLEMENTED;
>+}
>+
>+static inline NTSTATUS fast_sleep_srw( RTL_CONDITION_VARIABLE *variable,
>+    RTL_SRWLOCK *lock, const LARGE_INTEGER *timeout, ULONG flags )
>+{
>+    return STATUS_NOT_IMPLEMENTED;
>+}
>+
>+static inline NTSTATUS fast_wake( RTL_CONDITION_VARIABLE *variable, int val )
>+{
>+    return STATUS_NOT_IMPLEMENTED;
>+}
>+#endif
>+
> 
> static inline int interlocked_dec_if_nonzero( int *dest )
> {
>@@ -1868,8 +2005,11 @@
>  */
> void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable )
> {
>-    if (interlocked_dec_if_nonzero( (int *)&variable->Ptr ))
>-        NtReleaseKeyedEvent( 0, &variable->Ptr, FALSE, NULL );
>+    if (fast_wake( variable, 1 ) == STATUS_NOT_IMPLEMENTED)
>+    {
>+        if (interlocked_dec_if_nonzero( (int *)&variable->Ptr ))
>+            NtReleaseKeyedEvent( keyed_event, &variable->Ptr, FALSE, NULL );
>+    }
> }
> 
> /***********************************************************************
>@@ -1879,9 +2019,12 @@
>  */
> void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable )
> {
>-    int val = interlocked_xchg( (int *)&variable->Ptr, 0 );
>-    while (val-- > 0)
>-        NtReleaseKeyedEvent( 0, &variable->Ptr, FALSE, NULL );
>+    if (fast_wake( variable, INT_MAX ) == STATUS_NOT_IMPLEMENTED)
>+    {
>+        int val = interlocked_xchg( (int *)&variable->Ptr, 0 );
>+        while (val-- > 0)
>+            NtReleaseKeyedEvent( keyed_event, &variable->Ptr, FALSE, NULL );
>+    }
> }
> 
> /***********************************************************************
>@@ -1903,17 +2046,24 @@
>                                              const LARGE_INTEGER *timeout )
> {
>     NTSTATUS status;
>-    interlocked_xchg_add( (int *)&variable->Ptr, 1 );
>-    RtlLeaveCriticalSection( crit );
> 
>-    status = NtWaitForKeyedEvent( 0, &variable->Ptr, FALSE, timeout );
>-    if (status != STATUS_SUCCESS)
>+    if ((status = fast_sleep_cs( variable, crit, timeout )) == STATUS_NOT_IMPLEMENTED)
>     {
>-        if (!interlocked_dec_if_nonzero( (int *)&variable->Ptr ))
>-            status = NtWaitForKeyedEvent( 0, &variable->Ptr, FALSE, NULL );
>-    }
>+        interlocked_xchg_add( (int *)&variable->Ptr, 1 );
>+        RtlLeaveCriticalSection( crit );
> 
>-    RtlEnterCriticalSection( crit );
>+        status = NtWaitForKeyedEvent( keyed_event, &variable->Ptr, FALSE, timeout );
>+        if (status != STATUS_SUCCESS)
>+        {
>+            if (!interlocked_dec_if_nonzero( (int *)&variable->Ptr ))
>+                status = NtWaitForKeyedEvent( keyed_event, &variable->Ptr, FALSE, NULL );
>+        }
>+
>+        else if (status != STATUS_SUCCESS)
>+            interlocked_dec_if_nonzero( (int *)&variable->Ptr );
>+
>+        RtlEnterCriticalSection( crit );
>+    }
>     return status;
> }
> 
>@@ -1940,24 +2090,31 @@
>                                               const LARGE_INTEGER *timeout, ULONG flags )
> {
>     NTSTATUS status;
>-    interlocked_xchg_add( (int *)&variable->Ptr, 1 );
>-
>-    if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
>-        RtlReleaseSRWLockShared( lock );
>-    else
>-        RtlReleaseSRWLockExclusive( lock );
> 
>-    status = NtWaitForKeyedEvent( 0, &variable->Ptr, FALSE, timeout );
>-    if (status != STATUS_SUCCESS)
>+    if ((status = fast_sleep_srw( variable, lock, timeout, flags )) == STATUS_NOT_IMPLEMENTED)
>     {
>-        if (!interlocked_dec_if_nonzero( (int *)&variable->Ptr ))
>-            status = NtWaitForKeyedEvent( 0, &variable->Ptr, FALSE, NULL );
>-    }
>+        interlocked_xchg_add( (int *)&variable->Ptr, 1 );
> 
>-    if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
>-        RtlAcquireSRWLockShared( lock );
>-    else
>-        RtlAcquireSRWLockExclusive( lock );
>+        if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
>+            RtlReleaseSRWLockShared( lock );
>+        else
>+            RtlReleaseSRWLockExclusive( lock );
>+
>+        status = NtWaitForKeyedEvent( keyed_event, &variable->Ptr, FALSE, timeout );
>+        if (status != STATUS_SUCCESS)
>+        {
>+            if (!interlocked_dec_if_nonzero( (int *)&variable->Ptr ))
>+                status = NtWaitForKeyedEvent( keyed_event, &variable->Ptr, FALSE, NULL );
>+        }
>+
>+        else if (status != STATUS_SUCCESS)
>+            interlocked_dec_if_nonzero( (int *)&variable->Ptr );
>+
>+        if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
>+            RtlAcquireSRWLockShared( lock );
>+        else
>+            RtlAcquireSRWLockExclusive( lock );
>+    }
>     return status;
> }
> 
>@@ -1987,6 +2144,89 @@
>     return FALSE;
> }
> 
>+#ifdef __linux__
>+
>+static int addr_futex_table[256];
>+
>+static inline int *hash_addr( const void *addr )
>+{
>+    ULONG_PTR val = (ULONG_PTR)addr;
>+
>+    return &addr_futex_table[(val >> 2) & 255];
>+}
>+
>+static inline NTSTATUS fast_wait_addr( const void *addr, const void *cmp, SIZE_T size,
>+                                       const LARGE_INTEGER *timeout )
>+{
>+    int *futex;
>+    int val;
>+    LARGE_INTEGER now;
>+    timeout_t diff;
>+    struct timespec timespec;
>+    int ret;
>+
>+    if (!use_futexes())
>+        return STATUS_NOT_IMPLEMENTED;
>+
>+    futex = hash_addr( addr );
>+
>+    /* We must read the previous value of the futex before checking the value
>+     * of the address being waited on. That way, if we receive a wake between
>+     * now and waiting on the futex, we know that val will have changed. */
>+    val = interlocked_cmpxchg( futex, 0, 0 );
>+    if (!compare_addr( addr, cmp, size ))
>+        return STATUS_SUCCESS;
>+
>+    if (timeout)
>+    {
>+        if (timeout->QuadPart > 0)
>+        {
>+            NtQuerySystemTime( &now );
>+            diff = timeout->QuadPart - now.QuadPart;
>+        }
>+        else
>+            diff = -timeout->QuadPart;
>+
>+        timespec.tv_sec  = diff / 1000000;
>+        timespec.tv_nsec = diff % 1000000;
>+
>+        ret = futex_wait( futex, val, &timespec );
>+    }
>+    else
>+        ret = futex_wait( futex, val, NULL );
>+
>+    if (ret == -1 && errno == ETIMEDOUT)
>+        return STATUS_TIMEOUT;
>+    return STATUS_SUCCESS;
>+}
>+
>+static inline NTSTATUS fast_wake_addr( const void *addr )
>+{
>+    int *futex;
>+
>+    if (!use_futexes())
>+        return STATUS_NOT_IMPLEMENTED;
>+
>+    futex = hash_addr( addr );
>+
>+    interlocked_xchg_add( futex, 1 );
>+
>+    futex_wake( futex, INT_MAX );
>+    return STATUS_SUCCESS;
>+}
>+#else
>+static inline NTSTATUS fast_wait_addr( const void *addr, const void *cmp, SIZE_T size,
>+                                       const LARGE_INTEGER *timeout )
>+{
>+    return STATUS_NOT_IMPLEMENTED;
>+}
>+
>+static inline NTSTATUS fast_wake_addr( const void *addr )
>+{
>+    return STATUS_NOT_IMPLEMENTED;
>+}
>+#endif
>+
> /***********************************************************************
>  *           RtlWaitOnAddress   (NTDLL.@)
>  */
>@@ -2005,6 +2245,9 @@
>     if (size != 1 && size != 2 && size != 4 && size != 8)
>         return STATUS_INVALID_PARAMETER;
> 
>+    if ((ret = fast_wait_addr( addr, cmp, size, timeout )) != STATUS_NOT_IMPLEMENTED)
>+        return ret;
>+
>     select_op.keyed_event.op     = SELECT_KEYED_EVENT_WAIT;
>     select_op.keyed_event.handle = wine_server_obj_handle( keyed_event );
>     select_op.keyed_event.key    = wine_server_client_ptr( addr );
>@@ -2059,6 +2302,9 @@
>  */
> void WINAPI RtlWakeAddressAll( const void *addr )
> {
>+    if (fast_wake_addr( addr ) != STATUS_NOT_IMPLEMENTED)
>+        return;
>+
>     RtlEnterCriticalSection( &addr_section );
>     while (NtReleaseKeyedEvent( 0, addr, 0, &zero_timeout ) == STATUS_SUCCESS) {}
>     RtlLeaveCriticalSection( &addr_section );
>@@ -2069,6 +2315,9 @@
>  */
> void WINAPI RtlWakeAddressSingle( const void *addr )
> {
>+    if (fast_wake_addr( addr ) != STATUS_NOT_IMPLEMENTED)
>+        return;
>+
>     RtlEnterCriticalSection( &addr_section );
>     NtReleaseKeyedEvent( 0, addr, 0, &zero_timeout );
>     RtlLeaveCriticalSection( &addr_section );

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.


More information about the wine-bugs mailing list