https://bugs.winehq.org/show_bug.cgi?id=46252
--- Comment #10 from rawfox rawfox@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, ×pec );
- }
- 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, ×pec );
- }
- 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 );