Kernel/Mutex: Don't duplicate threads in the mutex waiter list.
Exit from AddMutexWaiter early if the thread is already waiting for a mutex owned by the owner thread. This accounts for the possibility of a thread that is waiting on a condition variable being awakened twice in a row. Also added more validation asserts. This should fix one of the random crashes in Breath Of The Wild.
This commit is contained in:
parent
5926fbd3d7
commit
5224cc49c4
|
@ -706,8 +706,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||||
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
|
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
|
||||||
auto owner = g_handle_table.Get<Thread>(owner_handle);
|
auto owner = g_handle_table.Get<Thread>(owner_handle);
|
||||||
ASSERT(owner);
|
ASSERT(owner);
|
||||||
ASSERT(thread->status != ThreadStatus::Running);
|
ASSERT(thread->status == ThreadStatus::WaitMutex);
|
||||||
thread->status = ThreadStatus::WaitMutex;
|
|
||||||
thread->wakeup_callback = nullptr;
|
thread->wakeup_callback = nullptr;
|
||||||
|
|
||||||
owner->AddMutexWaiter(thread);
|
owner->AddMutexWaiter(thread);
|
||||||
|
|
|
@ -419,12 +419,33 @@ VAddr Thread::GetCommandBufferAddress() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
|
void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
|
||||||
|
if (thread->lock_owner == this) {
|
||||||
|
// If the thread is already waiting for this thread to release the mutex, ensure that the
|
||||||
|
// waiters list is consistent and return without doing anything.
|
||||||
|
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
|
||||||
|
ASSERT(itr != wait_mutex_threads.end());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A thread can't wait on two different mutexes at the same time.
|
||||||
|
ASSERT(thread->lock_owner == nullptr);
|
||||||
|
|
||||||
|
// Ensure that the thread is not already in the list of mutex waiters
|
||||||
|
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
|
||||||
|
ASSERT(itr == wait_mutex_threads.end());
|
||||||
|
|
||||||
thread->lock_owner = this;
|
thread->lock_owner = this;
|
||||||
wait_mutex_threads.emplace_back(std::move(thread));
|
wait_mutex_threads.emplace_back(std::move(thread));
|
||||||
UpdatePriority();
|
UpdatePriority();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
|
void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
|
||||||
|
ASSERT(thread->lock_owner == this);
|
||||||
|
|
||||||
|
// Ensure that the thread is in the list of mutex waiters
|
||||||
|
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
|
||||||
|
ASSERT(itr != wait_mutex_threads.end());
|
||||||
|
|
||||||
boost::remove_erase(wait_mutex_threads, thread);
|
boost::remove_erase(wait_mutex_threads, thread);
|
||||||
thread->lock_owner = nullptr;
|
thread->lock_owner = nullptr;
|
||||||
UpdatePriority();
|
UpdatePriority();
|
||||||
|
|
Reference in New Issue