Просмотр исходного кода

Kernel: Correct Results in Condition Variables and Mutexes

Fernando Sahmkow 6 лет назад
Родитель
Сommit
44e09e5f21
3 измененных файлов с 17 добавлено и 24 удалено
  1. 7 6
      src/core/hle/kernel/kernel.cpp
  2. 1 0
      src/core/hle/kernel/mutex.cpp
  3. 9 18
      src/core/hle/kernel/svc.cpp

+ 7 - 6
src/core/hle/kernel/kernel.cpp

@@ -15,6 +15,7 @@
 #include "core/core_timing_util.h"
 #include "core/hle/kernel/address_arbiter.h"
 #include "core/hle/kernel/client_port.h"
+#include "core/hle/kernel/errors.h"
 #include "core/hle/kernel/handle_table.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/process.h"
@@ -60,12 +61,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
         if (thread->HasWakeupCallback()) {
             resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
         }
-    }
-
-    if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 ||
-        thread->GetWaitHandle() != 0) {
-        ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex ||
-               thread->GetStatus() == ThreadStatus::WaitCondVar);
+    } else if (thread->GetStatus() == ThreadStatus::WaitMutex ||
+               thread->GetStatus() == ThreadStatus::WaitCondVar) {
         thread->SetMutexWaitAddress(0);
         thread->SetCondVarWaitAddress(0);
         thread->SetWaitHandle(0);
@@ -85,6 +82,10 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
     }
 
     if (resume) {
+        if (thread->GetStatus() == ThreadStatus::WaitCondVar ||
+            thread->GetStatus() == ThreadStatus::WaitArb) {
+            thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
+        }
         thread->ResumeFromWait();
     }
 }

+ 1 - 0
src/core/hle/kernel/mutex.cpp

@@ -139,6 +139,7 @@ ResultCode Mutex::Release(VAddr address) {
     thread->SetCondVarWaitAddress(0);
     thread->SetMutexWaitAddress(0);
     thread->SetWaitHandle(0);
+    thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
 
     system.PrepareReschedule();
 

+ 9 - 18
src/core/hle/kernel/svc.cpp

@@ -1677,18 +1677,20 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
 
         // Atomically read the value of the mutex.
         u32 mutex_val = 0;
+        u32 update_val = 0;
+        const VAddr mutex_address = thread->GetMutexWaitAddress();
         do {
-            monitor.SetExclusive(current_core, thread->GetMutexWaitAddress());
+            monitor.SetExclusive(current_core, mutex_address);
 
             // If the mutex is not yet acquired, acquire it.
-            mutex_val = Memory::Read32(thread->GetMutexWaitAddress());
+            mutex_val = Memory::Read32(mutex_address);
 
             if (mutex_val != 0) {
-                monitor.ClearExclusive();
-                break;
+                update_val = mutex_val | Mutex::MutexHasWaitersFlag;
+            } else {
+                update_val = thread->GetWaitHandle();
             }
-        } while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
-                                           thread->GetWaitHandle()));
+        } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val));
         if (mutex_val == 0) {
             // We were able to acquire the mutex, resume this thread.
             ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
@@ -1702,20 +1704,9 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
             thread->SetLockOwner(nullptr);
             thread->SetMutexWaitAddress(0);
             thread->SetWaitHandle(0);
+            thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
             system.PrepareReschedule(thread->GetProcessorID());
         } else {
-            // Atomically signal that the mutex now has a waiting thread.
-            do {
-                monitor.SetExclusive(current_core, thread->GetMutexWaitAddress());
-
-                // Ensure that the mutex value is still what we expect.
-                u32 value = Memory::Read32(thread->GetMutexWaitAddress());
-                // TODO(Subv): When this happens, the kernel just clears the exclusive state and
-                // retries the initial read for this thread.
-                ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case");
-            } while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
-                                               mutex_val | Mutex::MutexHasWaitersFlag));
-
             // The mutex is already owned by some other thread, make this thread wait on it.
             const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
             const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();