diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 663d0f4b6e..6395d9c721 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "core/core.h" +#include "core/core_cpu.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" @@ -78,7 +79,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, // thread. ASSERT(requesting_thread == current_thread); - const u32 addr_value = Memory::Read32(address); + u32 addr_value = Memory::Read32(address); // If the mutex isn't being held, just return success. if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { @@ -89,6 +90,20 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, return ERR_INVALID_HANDLE; } + // This a workaround where an unknown bug writes the mutex value to give ownership to a cond var + // waiting thread. + if (holding_thread->GetStatus() == ThreadStatus::WaitCondVar) { + if (holding_thread->GetMutexWaitAddress() == address) { + Release(address, holding_thread.get()); + addr_value = Memory::Read32(address); + if (addr_value == 0) + return RESULT_SUCCESS; + else { + holding_thread = handle_table.Get(addr_value & Mutex::MutexOwnerMask); + } + } + } + // Wait until the mutex is released current_thread->SetMutexWaitAddress(address); current_thread->SetWaitHandle(requesting_thread_handle); @@ -104,14 +119,13 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, return RESULT_SUCCESS; } -ResultCode Mutex::Release(VAddr address) { +ResultCode Mutex::Release(VAddr address, Thread* holding_thread) { // The mutex address must be 4-byte aligned if ((address % sizeof(u32)) != 0) { return ERR_INVALID_ADDRESS; } - auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); - auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address); + auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(holding_thread, address); // There are no more threads waiting for the mutex, release it completely. if (thread == nullptr) { @@ -120,7 +134,7 @@ ResultCode Mutex::Release(VAddr address) { } // Transfer the ownership of the mutex from the previous owner to the new one. - TransferMutexOwnership(address, current_thread, thread); + TransferMutexOwnership(address, holding_thread, thread); u32 mutex_value = thread->GetWaitHandle(); @@ -141,7 +155,10 @@ ResultCode Mutex::Release(VAddr address) { thread->SetWaitHandle(0); thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - system.PrepareReschedule(); + if (thread->GetProcessorID() >= 0) + system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); + if (holding_thread->GetProcessorID() >= 0) + system.CpuCore(holding_thread->GetProcessorID()).PrepareReschedule(); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index b904de2e82..8cd990d9e6 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -29,7 +29,7 @@ public: Handle requesting_thread_handle); /// Releases the mutex at the specified address. - ResultCode Release(VAddr address); + ResultCode Release(VAddr address, Thread* holding_thread); private: Core::System& system; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f64236be1a..d8b4559a7b 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -578,7 +578,8 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { } auto* const current_process = system.Kernel().CurrentProcess(); - return current_process->GetMutex().Release(mutex_addr); + return current_process->GetMutex().Release(mutex_addr, + system.CurrentScheduler().GetCurrentThread()); } enum class BreakType : u32 { @@ -1615,12 +1616,14 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add SharedPtr thread = handle_table.Get(thread_handle); ASSERT(thread); - const auto release_result = current_process->GetMutex().Release(mutex_addr); + SharedPtr current_thread = system.CurrentScheduler().GetCurrentThread(); + + const auto release_result = + current_process->GetMutex().Release(mutex_addr, current_thread.get()); if (release_result.IsError()) { return release_result; } - SharedPtr current_thread = system.CurrentScheduler().GetCurrentThread(); current_thread->SetCondVarWaitAddress(condition_variable_addr); current_thread->SetMutexWaitAddress(mutex_addr); current_thread->SetWaitHandle(thread_handle);