Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48389 Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/kernel32/tests/sync.c | 76 ++++++++++++++---------- dlls/ntdll/sync.c | 117 +++++++++++++++++++++++++++++++------ 2 files changed, 144 insertions(+), 49 deletions(-)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c index 6426d7cc6fb..8cdb7f10887 100644 --- a/dlls/kernel32/tests/sync.c +++ b/dlls/kernel32/tests/sync.c @@ -2241,10 +2241,18 @@ static void test_condvars_consumer_producer(void)
/* Sample test for some sequence of events happening, sequenced using "condvar_seq" */ static DWORD condvar_seq = 0; -static CONDITION_VARIABLE condvar_base = CONDITION_VARIABLE_INIT; +static CONDITION_VARIABLE aligned_cv; static CRITICAL_SECTION condvar_crit; static SRWLOCK condvar_srwlock;
+#include "pshpack1.h" +static struct +{ + char c; + CONDITION_VARIABLE cv; +} unaligned_cv; +#include "poppack.h" + /* Sequence of wake/sleep to check boundary conditions: * 0: init * 1: producer emits a WakeConditionVariable without consumer waiting. @@ -2264,28 +2272,31 @@ static SRWLOCK condvar_srwlock; * 12: producer (shared) wakes up consumer (shared) * 13: end */ -static DWORD WINAPI condvar_base_producer(LPVOID x) { +static DWORD WINAPI condvar_base_producer(void *arg) +{ + CONDITION_VARIABLE *cv = arg; + while (condvar_seq < 1) Sleep(1);
- pWakeConditionVariable (&condvar_base); + pWakeConditionVariable(cv); condvar_seq = 2;
while (condvar_seq < 3) Sleep(1); - pWakeAllConditionVariable (&condvar_base); + pWakeAllConditionVariable(cv); condvar_seq = 4;
while (condvar_seq < 5) Sleep(1); EnterCriticalSection (&condvar_crit); - pWakeConditionVariable (&condvar_base); + pWakeConditionVariable(cv); LeaveCriticalSection (&condvar_crit); while (condvar_seq < 6) Sleep(1); EnterCriticalSection (&condvar_crit); - pWakeAllConditionVariable (&condvar_base); + pWakeAllConditionVariable(cv); LeaveCriticalSection (&condvar_crit);
while (condvar_seq < 8) Sleep(1); EnterCriticalSection (&condvar_crit); - pWakeConditionVariable (&condvar_base); + pWakeConditionVariable(cv); Sleep(50); LeaveCriticalSection (&condvar_crit);
@@ -2295,36 +2306,38 @@ static DWORD WINAPI condvar_base_producer(LPVOID x) {
while (condvar_seq < 9) Sleep(1); pAcquireSRWLockExclusive(&condvar_srwlock); - pWakeConditionVariable(&condvar_base); + pWakeConditionVariable(cv); pReleaseSRWLockExclusive(&condvar_srwlock);
while (condvar_seq < 10) Sleep(1); pAcquireSRWLockExclusive(&condvar_srwlock); - pWakeConditionVariable(&condvar_base); + pWakeConditionVariable(cv); pReleaseSRWLockExclusive(&condvar_srwlock);
while (condvar_seq < 11) Sleep(1); pAcquireSRWLockShared(&condvar_srwlock); - pWakeConditionVariable(&condvar_base); + pWakeConditionVariable(cv); pReleaseSRWLockShared(&condvar_srwlock);
while (condvar_seq < 12) Sleep(1); Sleep(50); /* ensure that consumer waits for cond variable */ pAcquireSRWLockShared(&condvar_srwlock); - pWakeConditionVariable(&condvar_base); + pWakeConditionVariable(cv); pReleaseSRWLockShared(&condvar_srwlock);
return 0; }
-static DWORD WINAPI condvar_base_consumer(LPVOID x) { +static DWORD WINAPI condvar_base_consumer(void *arg) +{ + CONDITION_VARIABLE *cv = arg; BOOL ret;
while (condvar_seq < 2) Sleep(1);
/* wake was emitted, but we were not sleeping */ EnterCriticalSection (&condvar_crit); - ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 10); + ret = pSleepConditionVariableCS(cv, &condvar_crit, 10); LeaveCriticalSection (&condvar_crit); ok (!ret, "SleepConditionVariableCS should return FALSE on out of band wake\n"); ok (GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableCS should return ERROR_TIMEOUT on out of band wake, not %d\n", GetLastError()); @@ -2334,33 +2347,33 @@ static DWORD WINAPI condvar_base_consumer(LPVOID x) {
/* wake all was emitted, but we were not sleeping */ EnterCriticalSection (&condvar_crit); - ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 10); + ret = pSleepConditionVariableCS(cv, &condvar_crit, 10); LeaveCriticalSection (&condvar_crit); ok (!ret, "SleepConditionVariableCS should return FALSE on out of band wake\n"); ok (GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableCS should return ERROR_TIMEOUT on out of band wake, not %d\n", GetLastError());
EnterCriticalSection (&condvar_crit); condvar_seq = 5; - ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 200); + ret = pSleepConditionVariableCS(cv, &condvar_crit, 200); LeaveCriticalSection (&condvar_crit); ok (ret, "SleepConditionVariableCS should return TRUE on good wake\n");
EnterCriticalSection (&condvar_crit); condvar_seq = 6; - ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 200); + ret = pSleepConditionVariableCS(cv, &condvar_crit, 200); LeaveCriticalSection (&condvar_crit); ok (ret, "SleepConditionVariableCS should return TRUE on good wakeall\n"); condvar_seq = 7;
EnterCriticalSection (&condvar_crit); - ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 10); + ret = pSleepConditionVariableCS(cv, &condvar_crit, 10); LeaveCriticalSection (&condvar_crit); ok (!ret, "SleepConditionVariableCS should return FALSE on out of band wake\n"); ok (GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableCS should return ERROR_TIMEOUT on out of band wake, not %d\n", GetLastError());
EnterCriticalSection (&condvar_crit); condvar_seq = 8; - ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 20); + ret = pSleepConditionVariableCS(cv, &condvar_crit, 20); LeaveCriticalSection (&condvar_crit); ok (ret, "SleepConditionVariableCS should still return TRUE on crit unlock delay\n");
@@ -2374,25 +2387,25 @@ static DWORD WINAPI condvar_base_consumer(LPVOID x) {
pAcquireSRWLockExclusive(&condvar_srwlock); condvar_seq = 9; - ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 200, 0); + ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 200, 0); pReleaseSRWLockExclusive(&condvar_srwlock); ok (ret, "pSleepConditionVariableSRW should return TRUE on good wake\n");
pAcquireSRWLockShared(&condvar_srwlock); condvar_seq = 10; - ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 200, CONDITION_VARIABLE_LOCKMODE_SHARED); + ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 200, CONDITION_VARIABLE_LOCKMODE_SHARED); pReleaseSRWLockShared(&condvar_srwlock); ok (ret, "pSleepConditionVariableSRW should return TRUE on good wake\n");
pAcquireSRWLockExclusive(&condvar_srwlock); condvar_seq = 11; - ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 200, 0); + ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 200, 0); pReleaseSRWLockExclusive(&condvar_srwlock); ok (ret, "pSleepConditionVariableSRW should return TRUE on good wake\n");
pAcquireSRWLockShared(&condvar_srwlock); condvar_seq = 12; - ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 200, CONDITION_VARIABLE_LOCKMODE_SHARED); + ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 200, CONDITION_VARIABLE_LOCKMODE_SHARED); pReleaseSRWLockShared(&condvar_srwlock); ok (ret, "pSleepConditionVariableSRW should return TRUE on good wake\n");
@@ -2400,12 +2413,12 @@ static DWORD WINAPI condvar_base_consumer(LPVOID x) { return 0; }
-static void test_condvars_base(void) { +static void test_condvars_base(RTL_CONDITION_VARIABLE *cv) +{ HANDLE hp, hc; DWORD dummy; BOOL ret;
- if (!pInitializeConditionVariable) { /* function is not yet in XP, only in newer Windows */ win_skip("no condition variable support.\n"); @@ -2418,7 +2431,7 @@ static void test_condvars_base(void) { pInitializeSRWLock(&condvar_srwlock);
EnterCriticalSection (&condvar_crit); - ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 10); + ret = pSleepConditionVariableCS(cv, &condvar_crit, 10); LeaveCriticalSection (&condvar_crit);
ok (!ret, "SleepConditionVariableCS should return FALSE on untriggered condvar\n"); @@ -2427,23 +2440,23 @@ static void test_condvars_base(void) { if (pInitializeSRWLock) { pAcquireSRWLockExclusive(&condvar_srwlock); - ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 10, 0); + ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 10, 0); pReleaseSRWLockExclusive(&condvar_srwlock);
ok(!ret, "SleepConditionVariableSRW should return FALSE on untriggered condvar\n"); ok(GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableSRW should return ERROR_TIMEOUT on untriggered condvar, not %d\n", GetLastError());
pAcquireSRWLockShared(&condvar_srwlock); - ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 10, CONDITION_VARIABLE_LOCKMODE_SHARED); + ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 10, CONDITION_VARIABLE_LOCKMODE_SHARED); pReleaseSRWLockShared(&condvar_srwlock);
ok(!ret, "SleepConditionVariableSRW should return FALSE on untriggered condvar\n"); ok(GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableSRW should return ERROR_TIMEOUT on untriggered condvar, not %d\n", GetLastError()); }
- - hp = CreateThread(NULL, 0, condvar_base_producer, NULL, 0, &dummy); - hc = CreateThread(NULL, 0, condvar_base_consumer, NULL, 0, &dummy); + condvar_seq = 0; + hp = CreateThread(NULL, 0, condvar_base_producer, cv, 0, &dummy); + hc = CreateThread(NULL, 0, condvar_base_consumer, cv, 0, &dummy);
condvar_seq = 1; /* go */
@@ -3252,7 +3265,8 @@ START_TEST(sync) test_WaitForSingleObject(); test_WaitForMultipleObjects(); test_initonce(); - test_condvars_base(); + test_condvars_base(&aligned_cv); + test_condvars_base(&unaligned_cv.cv); test_condvars_consumer_producer(); test_srwlock_base(); test_srwlock_example(); diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 62a60362768..d7319405889 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -115,6 +115,16 @@ static inline int use_futexes(void) return supported; }
+static int *get_futex(void **ptr) +{ + if (sizeof(void *) == 8) + return (int *)((((ULONG_PTR)ptr) + 3) & ~3); + else if (!(((ULONG_PTR)ptr) & 3)) + return (int *)ptr; + else + return NULL; +} + static void timespec_from_timeout( struct timespec *timespec, const LARGE_INTEGER *timeout ) { LARGE_INTEGER now; @@ -2227,35 +2237,98 @@ BOOLEAN WINAPI RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock ) }
#ifdef __linux__ -static NTSTATUS fast_wait_cv( RTL_CONDITION_VARIABLE *variable, int val, const LARGE_INTEGER *timeout ) +static NTSTATUS fast_wait_cv( int *futex, 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( ×pec, timeout ); - ret = futex_wait( (int *)&variable->Ptr, val, ×pec ); + ret = futex_wait( futex, val, ×pec ); } else - ret = futex_wait( (int *)&variable->Ptr, val, NULL ); + ret = futex_wait( futex, val, NULL );
if (ret == -1 && errno == ETIMEDOUT) return STATUS_TIMEOUT; return STATUS_WAIT_0; }
+static NTSTATUS fast_sleep_cs_cv( RTL_CONDITION_VARIABLE *variable, + RTL_CRITICAL_SECTION *cs, const LARGE_INTEGER *timeout ) +{ + NTSTATUS status; + int val, *futex; + + if (!use_futexes()) + return STATUS_NOT_IMPLEMENTED; + + if (!(futex = get_futex( &variable->Ptr ))) + return STATUS_NOT_IMPLEMENTED; + + val = *futex; + + RtlLeaveCriticalSection( cs ); + status = fast_wait_cv( futex, val, timeout ); + RtlEnterCriticalSection( cs ); + return status; +} + +static NTSTATUS fast_sleep_srw_cv( RTL_CONDITION_VARIABLE *variable, + RTL_SRWLOCK *lock, const LARGE_INTEGER *timeout, ULONG flags ) +{ + NTSTATUS status; + int val, *futex; + + if (!use_futexes()) + return STATUS_NOT_IMPLEMENTED; + + if (!(futex = get_futex( &variable->Ptr ))) + return STATUS_NOT_IMPLEMENTED; + + val = *futex; + + if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED) + RtlReleaseSRWLockShared( lock ); + else + RtlReleaseSRWLockExclusive( lock ); + + status = fast_wait_cv( futex, val, timeout ); + + if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED) + RtlAcquireSRWLockShared( lock ); + else + RtlAcquireSRWLockExclusive( lock ); + return status; +} + static NTSTATUS fast_wake_cv( RTL_CONDITION_VARIABLE *variable, int count ) { + int *futex; + if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
- futex_wake( (int *)&variable->Ptr, count ); + if (!(futex = get_futex( &variable->Ptr ))) + return STATUS_NOT_IMPLEMENTED; + + interlocked_xchg_add( futex, 1 ); + futex_wake( futex, count ); return STATUS_SUCCESS; } #else +static NTSTATUS fast_sleep_srw_cv( RTL_CONDITION_VARIABLE *variable, + RTL_SRWLOCK *lock, const LARGE_INTEGER *timeout, ULONG flags ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS fast_sleep_cs_cv( RTL_CONDITION_VARIABLE *variable, + RTL_CRITICAL_SECTION *cs, const LARGE_INTEGER *timeout ) +{ + return STATUS_NOT_IMPLEMENTED; +} + static NTSTATUS fast_wait_cv( RTL_CONDITION_VARIABLE *variable, int val, const LARGE_INTEGER *timeout ) { return STATUS_NOT_IMPLEMENTED; @@ -2300,9 +2373,11 @@ void WINAPI RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE *variable ) */ void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable ) { - interlocked_xchg_add( (int *)&variable->Ptr, 1 ); if (fast_wake_cv( variable, 1 ) == STATUS_NOT_IMPLEMENTED) + { + interlocked_xchg_add( (int *)&variable->Ptr, 1 ); RtlWakeAddressSingle( variable ); + } }
/*********************************************************************** @@ -2312,9 +2387,11 @@ void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable ) */ void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable ) { - interlocked_xchg_add( (int *)&variable->Ptr, 1 ); if (fast_wake_cv( variable, INT_MAX ) == STATUS_NOT_IMPLEMENTED) + { + interlocked_xchg_add( (int *)&variable->Ptr, 1 ); RtlWakeAddressAll( variable ); + } }
/*********************************************************************** @@ -2336,15 +2413,15 @@ NTSTATUS WINAPI RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE *variable, R const LARGE_INTEGER *timeout ) { NTSTATUS status; - int val = *(int *)&variable->Ptr; - - RtlLeaveCriticalSection( crit ); + int val;
- if ((status = fast_wait_cv( variable, val, timeout )) == STATUS_NOT_IMPLEMENTED) - status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout ); + if ((status = fast_sleep_cs_cv( variable, crit, timeout )) != STATUS_NOT_IMPLEMENTED) + return status;
+ val = *(int *)&variable->Ptr; + RtlLeaveCriticalSection( crit ); + status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout ); RtlEnterCriticalSection( crit ); - return status; }
@@ -2371,15 +2448,19 @@ NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, const LARGE_INTEGER *timeout, ULONG flags ) { NTSTATUS status; - int val = *(int *)&variable->Ptr; + int val; + + if ((status = fast_sleep_srw_cv( variable, lock, timeout, flags )) != STATUS_NOT_IMPLEMENTED) + return status; + + val = *(int *)&variable->Ptr;
if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED) RtlReleaseSRWLockShared( lock ); else RtlReleaseSRWLockExclusive( lock );
- if ((status = fast_wait_cv( variable, val, timeout )) == STATUS_NOT_IMPLEMENTED) - status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout ); + status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED) RtlAcquireSRWLockShared( lock );