From e50ac69d04c81813a52ac8959db34c4c40f047cb Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Tue, 24 Jul 2018 11:26:39 -0600 Subject: [PATCH] ntdll: Add a futex-based condition variable implementation. Signed-off-by: Zebediah Figura --- dlls/ntdll/sync.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 11 deletions(-) diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 8e406ce..4752543 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -26,6 +26,7 @@ #include #include +#include #include #ifdef HAVE_SYS_TIME_H # include @@ -36,6 +37,9 @@ #ifdef HAVE_SYS_POLL_H # include #endif +#ifdef HAVE_SYS_SYSCALL_H +# include +#endif #ifdef HAVE_UNISTD_H # include #endif @@ -61,6 +65,102 @@ WINE_DEFAULT_DEBUG_CHANNEL(ntdll); HANDLE keyed_event = NULL; +#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, const LARGE_INTEGER *timeout) +{ + int val, ret; + + if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; + + if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) + { + struct timespec timespec; + LONGLONG duration; + + if (timeout->QuadPart > 0) + { + LARGE_INTEGER now; + NtQuerySystemTime( &now ); + duration = timeout->QuadPart - now.QuadPart; + } + else + duration = -timeout->QuadPart; + + timespec.tv_sec = duration / TICKSPERSEC; + timespec.tv_nsec = (duration % TICKSPERSEC) * 100; + do + val = *((int *)&variable->Ptr); + while (val && (ret = futex_wait( (int *)&variable->Ptr, val, ×pec )) == -1 + && errno == EAGAIN); + } + else + { + do + val = *((int *)&variable->Ptr); + while (val && (ret = futex_wait( (int *)&variable->Ptr, val, NULL )) == -1 + && errno == EAGAIN); + } + + if (ret == -1 && errno == ETIMEDOUT) return STATUS_TIMEOUT; + else if (ret == -1) FIXME("got errno %d %s\n", errno, strerror(errno)); + + return STATUS_WAIT_0; +} + +static inline NTSTATUS fast_wake( RTL_CONDITION_VARIABLE *variable, int val ) +{ + if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; + + futex_wake( (int *)&variable->Ptr, val ); + return STATUS_SUCCESS; +} + +#else +static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit ) +{ + return STATUS_NOT_IMPLEMENTED; +} +#endif + static inline int interlocked_dec_if_nonzero( int *dest ) { int val, tmp; @@ -1814,7 +1914,11 @@ void WINAPI RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE *variable ) void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable ) { if (interlocked_dec_if_nonzero( (int *)&variable->Ptr )) - NtReleaseKeyedEvent( keyed_event, &variable->Ptr, FALSE, NULL ); + { + NTSTATUS ret; + if ((ret = fast_wake( variable, 1 )) == STATUS_NOT_IMPLEMENTED) + NtReleaseKeyedEvent( keyed_event, &variable->Ptr, FALSE, NULL ); + } } /*********************************************************************** @@ -1825,8 +1929,15 @@ void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable ) void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable ) { int val = interlocked_xchg( (int *)&variable->Ptr, 0 ); - while (val-- > 0) - NtReleaseKeyedEvent( keyed_event, &variable->Ptr, FALSE, NULL ); + if (val) + { + NTSTATUS ret; + if ((ret = fast_wake( variable, INT_MAX )) == STATUS_NOT_IMPLEMENTED) + { + while (val-- > 0) + NtReleaseKeyedEvent( keyed_event, &variable->Ptr, FALSE, NULL ); + } + } } /*********************************************************************** @@ -1851,12 +1962,17 @@ NTSTATUS WINAPI RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE *variable, R interlocked_xchg_add( (int *)&variable->Ptr, 1 ); RtlLeaveCriticalSection( crit ); - status = NtWaitForKeyedEvent( keyed_event, &variable->Ptr, FALSE, timeout ); - if (status != STATUS_SUCCESS) + if ((status = fast_wait( variable, timeout )) == STATUS_NOT_IMPLEMENTED) { - if (!interlocked_dec_if_nonzero( (int *)&variable->Ptr )) - status = NtWaitForKeyedEvent( keyed_event, &variable->Ptr, FALSE, NULL ); + 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; @@ -1892,12 +2008,17 @@ NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, else RtlReleaseSRWLockExclusive( lock ); - status = NtWaitForKeyedEvent( keyed_event, &variable->Ptr, FALSE, timeout ); - if (status != STATUS_SUCCESS) + if ((status = fast_wait( variable, timeout )) == STATUS_NOT_IMPLEMENTED) { - if (!interlocked_dec_if_nonzero( (int *)&variable->Ptr )) - status = NtWaitForKeyedEvent( keyed_event, &variable->Ptr, FALSE, NULL ); + 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 ); -- 2.7.4