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:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user