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.
This commit is contained in:
Hamish Morrison
2015-05-07 23:20:54 +01:00
parent d1b6645b56
commit fb67dbf0a4
+18 -10
View File
@@ -131,8 +131,8 @@ user_mutex_lock_locked(int32* mutex, addr_t physicalAddress, const char* name,
status_t error = waitEntry.Wait(flags, timeout); status_t error = waitEntry.Wait(flags, timeout);
locker.Lock(); locker.Lock();
// dequeue // dequeue if we weren't woken up
if (!remove_user_mutex_entry(&entry)) { if (!entry.locked && !remove_user_mutex_entry(&entry)) {
// no one is waiting anymore -- clear the waiting flag // no one is waiting anymore -- clear the waiting flag
atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING); atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
} }
@@ -150,7 +150,13 @@ user_mutex_lock_locked(int32* mutex, addr_t physicalAddress, const char* name,
static void static void
user_mutex_unlock_locked(int32* mutex, addr_t physicalAddress, uint32 flags) user_mutex_unlock_locked(int32* mutex, addr_t physicalAddress, uint32 flags)
{ {
if (UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress)) { UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress);
if (entry == NULL) {
// no one is waiting -- clear locked flag
atomic_and(mutex, ~(int32)B_USER_MUTEX_LOCKED);
return;
}
// Someone is waiting -- set the locked flag. It might still be set, // Someone is waiting -- set the locked flag. It might still be set,
// but when using userland atomic operations, the caller will usually // but when using userland atomic operations, the caller will usually
// have cleared it already. // have cleared it already.
@@ -162,17 +168,19 @@ user_mutex_unlock_locked(int32* mutex, addr_t physicalAddress, uint32 flags)
if ((flags & B_USER_MUTEX_UNBLOCK_ALL) != 0 if ((flags & B_USER_MUTEX_UNBLOCK_ALL) != 0
|| (oldValue & B_USER_MUTEX_DISABLED) != 0) { || (oldValue & B_USER_MUTEX_DISABLED) != 0) {
// unblock all the other waiting threads as well // unblock and dequeue all the other waiting threads as well
for (UserMutexEntryList::Iterator it while (UserMutexEntry* otherEntry = entry->otherEntries.RemoveHead()) {
= entry->otherEntries.GetIterator();
UserMutexEntry* otherEntry = it.Next();) {
otherEntry->locked = true; otherEntry->locked = true;
otherEntry->condition.NotifyOne(); otherEntry->condition.NotifyOne();
} }
}
// dequeue the first thread and mark the mutex uncontended
sUserMutexTable.Remove(entry);
atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
} else { } else {
// no one is waiting -- clear locked flag bool otherWaiters = remove_user_mutex_entry(entry);
atomic_and(mutex, ~(int32)B_USER_MUTEX_LOCKED); if (!otherWaiters)
atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
} }
} }