From: Yuxuan Shui yshui@codeweavers.com
Allow thread that released an SRWLOCK to immediately re-acquire it. This makes the lock less fair but improves throughput. --- dlls/ntdll/sync.c | 49 +++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 17 deletions(-)
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 281d7943d03..b5a24fadb6a 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -496,8 +496,9 @@ enum srwlock_tag { };
enum srwlock_waiter_state { - SRWLOCK_WAITER_STATE_IS_EXCLUSIVE = 1, - /* ???? = 2, */ + SRWLOCK_WAITER_STATE_IS_EXCLUSIVE = 1 << 0, + /* Unknown on windows */ + SRWLOCK_WAITER_STATE_LOCKED = 1 << 1, SRWLOCK_WAITER_STATE_NOTIFIED = 1 << 2, }; struct DECLSPEC_ALIGN(16) srwlock_waiter { @@ -591,6 +592,7 @@ static void srwlock_wake(RTL_SRWLOCK *lock, struct srwlock_waiter * const last) RTL_SRWLOCK expected = srwlock_from_waiter(last, SRWLOCK_TAG_LIST_LOCKED | SRWLOCK_TAG_HAS_WAITERS); ULONG shared_owner_count = 0; + LONG waiter_state;
TRACE("%p\n", lock);
@@ -617,19 +619,29 @@ retry: for (i = new_head; i; i = i->next) i->head = new_head;
- tag = SRWLOCK_TAG_LOCKED; - if (shared_owner_count > 1) - tag |= SRWLOCK_TAG_HAS_MULTIPLE_OWNERS; - if (new_head == NULL) - new = srwlock_from_shared_owner_count(shared_owner_count, tag); - else + TRACE("%ld waiters will be woken, new_head = %p, head = %p\n", shared_owner_count, new_head, head); + + /* We only transfer the lock if there are more than 1 shared owners and with + * wait list present, because in that case, they are not capable of + * acquiring the lock all at the same time themselves. If there is only 1 + * owner, we wake it up without giving it the lock and let it acquire it the + * usual way. The purpose is so the current thread can immediately acquire + * the lock again if it wants to. This is less fair but helps with + * throughput. */ + if (shared_owner_count == 1 || !new_head) + shared_owner_count = 0; + + tag = 0; + if (shared_owner_count) + tag = SRWLOCK_TAG_HAS_MULTIPLE_OWNERS | SRWLOCK_TAG_LOCKED; + + if (new_head) { new = srwlock_from_waiter(last, tag | SRWLOCK_TAG_HAS_WAITERS); - if (shared_owner_count) - new_head->num_owners = shared_owner_count; - else - new_head->num_owners = SRWLOCK_WAITER_EXCLUSIVELY_LOCKED; + new_head->num_owners = shared_owner_count; } + else + new = srwlock_from_shared_owner_count(shared_owner_count, tag);
TRACE("new = %p, #owners = %ld\n", new.Ptr, shared_owner_count); read.Ptr = InterlockedCompareExchangePointer(&lock->Ptr, new.Ptr, expected.Ptr); @@ -643,7 +655,7 @@ retry: i->head = head; if (new_head) new_head->num_owners = SRWLOCK_WAITER_IS_NOT_HEAD; - head->num_owners = 1; + head->num_owners = SRWLOCK_WAITER_EXCLUSIVELY_LOCKED; new.Ptr = (PVOID)((ULONG_PTR)read.Ptr & ~(ULONG_PTR)SRWLOCK_TAG_LIST_LOCKED); if (InterlockedCompareExchangePointer(&lock->Ptr, new.Ptr, read.Ptr) != read.Ptr) goto retry; @@ -653,13 +665,16 @@ retry: } RtlWakeAddressSingle(&lock->Ptr);
+ waiter_state = SRWLOCK_WAITER_STATE_NOTIFIED; + if (tag & SRWLOCK_TAG_LOCKED) + waiter_state |= SRWLOCK_WAITER_STATE_LOCKED; while (head != new_head) { /* waking up the waiter will invalidate the waiter's entry in * the list. */ struct srwlock_waiter *next = head->next; ULONG_PTR thread_id = head->thread_id; - InterlockedOr((LONG *)&head->state, SRWLOCK_WAITER_STATE_NOTIFIED); + InterlockedOr((LONG *)&head->state, waiter_state); TRACE("waking %p\n", head); NtAlertThreadByThreadId((HANDLE)thread_id); head = next; @@ -735,7 +750,7 @@ static BOOLEAN srwlock_wait(RTL_SRWLOCK *lock, DWORD waiter_state, tag &= ~SRWLOCK_TAG_LIST_LOCKED;
new = srwlock_from_waiter(&waiter, tag); - TRACE("new = %p, tag = %x\n", new.Ptr, tag); + TRACE("new = %p, tag = %#x\n", new.Ptr, tag); } while( InterlockedCompareExchangePointer((void **)&lock->Ptr, new.Ptr, old.Ptr) != old.Ptr );
if (!(tag & (SRWLOCK_TAG_LOCKED | SRWLOCK_TAG_HAS_MULTIPLE_OWNERS))) @@ -752,8 +767,8 @@ static BOOLEAN srwlock_wait(RTL_SRWLOCK *lock, DWORD waiter_state, NtWaitForAlertByThreadId(NULL, NULL); };
- TRACE("acquired: %p\n", lock); - return TRUE; + TRACE("woken: %p %#lx\n", lock, waiter_state); + return (waiter_state & SRWLOCK_WAITER_STATE_LOCKED) != 0; }
static BOOLEAN srwlock_try_acquire_exclusive(RTL_SRWLOCK *lock, RTL_SRWLOCK *old)