From: Yuxuan Shui yshui@codeweavers.com
This comes from behavioral study of Windows, which doesn't seem to check if the lock is actually exclusively held, and just simply decrement the value stored in the lock. --- dlls/kernel32/tests/sync.c | 24 ++++++++++++++++++++++++ dlls/ntdll/sync.c | 8 +++++--- 2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c index 60180194b7a..4eaec87c52f 100644 --- a/dlls/kernel32/tests/sync.c +++ b/dlls/kernel32/tests/sync.c @@ -2533,6 +2533,29 @@ static void test_srwlock_example(void) trace("number of total exclusive accesses is %ld\n", srwlock_protected_value); }
+static void test_srwlock_quirk(void) +{ + union { SRWLOCK *s; LONG *l; } u = { &srwlock_example }; + + if (!pInitializeSRWLock) { + /* function is not yet in XP, only in newer Windows */ + win_skip("no srw lock support.\n"); + return; + } + + *u.l = 0; + pReleaseSRWLockExclusive(&srwlock_example); + ok(*u.l == 0xffffffff, "expected 0xffffffff, got %lx\n", *u.l); + + *u.l = 1; + pReleaseSRWLockExclusive(&srwlock_example); + ok(*u.l == 0, "expected 0x0, got %lx\n", *u.l); + + *u.l = 0x10000; + pReleaseSRWLockExclusive(&srwlock_example); + ok(*u.l == 0xffff, "expected 0xffff, got %lx\n", *u.l); +} + static DWORD WINAPI alertable_wait_thread(void *param) { HANDLE *semaphores = param; @@ -2887,6 +2910,7 @@ START_TEST(sync) test_srwlock_base(&aligned_srwlock); test_srwlock_base(&unaligned_srwlock.lock); test_srwlock_example(); + test_srwlock_quirk(); test_alertable_wait(); test_apc_deadlock(); test_crit_section(); diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index fa64917029a..3e71b7b7ee9 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -599,9 +599,11 @@ void WINAPI RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock ) old.s = *u.s; new = old;
- if (old.s.owners != -1) ERR("Lock %p is not owned exclusive!\n", lock); - - new.s.owners = 0; + if (old.s.owners != -1) { + ERR("Lock %p is not owned exclusive!\n", lock); + new.l = old.l - 1; /* this is what Windows does */ + } else + new.s.owners = 0; } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l);
if (new.s.exclusive_waiters)