From f59e7d7c70ff93693385cdb796c3ccc2af3bcd98 Mon Sep 17 00:00:00 2001
From: Hamish Morrison <hamishm53@gmail.com>
Date: Tue, 5 May 2015 00:51:37 +0100
Subject: [PATCH] user mutex: dequeue waiters when waking them up
* This prevents the same waiter being woken multiple times, before it
has a chance to run and dequeue itself.
---
src/system/kernel/locks/user_mutex.cpp | 54 +++++++++++++++++++---------------
1 file changed, 31 insertions(+), 23 deletions(-)
diff --git a/src/system/kernel/locks/user_mutex.cpp b/src/system/kernel/locks/user_mutex.cpp
index 42e2d93..c89f7f5 100644
a
|
b
|
user_mutex_lock_locked(int32* mutex, addr_t physicalAddress, const char* name,
|
131 | 131 | status_t error = waitEntry.Wait(flags, timeout); |
132 | 132 | locker.Lock(); |
133 | 133 | |
134 | | // dequeue |
135 | | if (!remove_user_mutex_entry(&entry)) { |
| 134 | // dequeue if we weren't woken up |
| 135 | if (!entry.locked && !remove_user_mutex_entry(&entry)) { |
136 | 136 | // no one is waiting anymore -- clear the waiting flag |
137 | 137 | atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING); |
138 | 138 | } |
… |
… |
user_mutex_lock_locked(int32* mutex, addr_t physicalAddress, const char* name,
|
150 | 150 | static void |
151 | 151 | user_mutex_unlock_locked(int32* mutex, addr_t physicalAddress, uint32 flags) |
152 | 152 | { |
153 | | if (UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress)) { |
154 | | // Someone is waiting -- set the locked flag. It might still be set, |
155 | | // but when using userland atomic operations, the caller will usually |
156 | | // have cleared it already. |
157 | | int32 oldValue = atomic_or(mutex, B_USER_MUTEX_LOCKED); |
158 | | |
159 | | // unblock the first thread |
160 | | entry->locked = true; |
161 | | entry->condition.NotifyOne(); |
162 | | |
163 | | if ((flags & B_USER_MUTEX_UNBLOCK_ALL) != 0 |
164 | | || (oldValue & B_USER_MUTEX_DISABLED) != 0) { |
165 | | // unblock all the other waiting threads as well |
166 | | for (UserMutexEntryList::Iterator it |
167 | | = entry->otherEntries.GetIterator(); |
168 | | UserMutexEntry* otherEntry = it.Next();) { |
169 | | otherEntry->locked = true; |
170 | | otherEntry->condition.NotifyOne(); |
171 | | } |
172 | | } |
173 | | } else { |
| 153 | UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress); |
| 154 | if (entry == NULL) { |
174 | 155 | // no one is waiting -- clear locked flag |
175 | 156 | atomic_and(mutex, ~(int32)B_USER_MUTEX_LOCKED); |
| 157 | return; |
| 158 | } |
| 159 | |
| 160 | // Someone is waiting -- set the locked flag. It might still be set, |
| 161 | // but when using userland atomic operations, the caller will usually |
| 162 | // have cleared it already. |
| 163 | int32 oldValue = atomic_or(mutex, B_USER_MUTEX_LOCKED); |
| 164 | |
| 165 | // unblock the first thread |
| 166 | entry->locked = true; |
| 167 | entry->condition.NotifyOne(); |
| 168 | |
| 169 | if ((flags & B_USER_MUTEX_UNBLOCK_ALL) != 0 |
| 170 | || (oldValue & B_USER_MUTEX_DISABLED) != 0) { |
| 171 | // unblock and dequeue all the other waiting threads as well |
| 172 | while (UserMutexEntry* otherEntry = entry->otherEntries.RemoveHead()) { |
| 173 | otherEntry->locked = true; |
| 174 | otherEntry->condition.NotifyOne(); |
| 175 | } |
| 176 | |
| 177 | // dequeue the first thread and mark the mutex uncontended |
| 178 | sUserMutexTable.Remove(entry); |
| 179 | atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING); |
| 180 | } else { |
| 181 | bool otherWaiters = remove_user_mutex_entry(entry); |
| 182 | if (!otherWaiters) |
| 183 | atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING); |
176 | 184 | } |
177 | 185 | } |
178 | 186 | |