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

Kernel: Correct behavior of Condition Variables to be more similar to real hardware.

This commit ensures cond var threads act exactly as they do in the real
console. The original implementation uses an RBTree and the behavior of
cond var threads is that at the same priority level they act like a
FIFO.
Fernando Sahmkow 6 лет назад
Родитель
Сommit
2d16507f9f

+ 3 - 0
src/core/hle/kernel/kernel.cpp

@@ -66,6 +66,9 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
         thread->SetMutexWaitAddress(0);
         thread->SetMutexWaitAddress(0);
         thread->SetCondVarWaitAddress(0);
         thread->SetCondVarWaitAddress(0);
         thread->SetWaitHandle(0);
         thread->SetWaitHandle(0);
+        if (thread->GetStatus() == ThreadStatus::WaitCondVar) {
+            thread->GetOwnerProcess()->RemoveConditionVariableThread(thread);
+        }
 
 
         auto* const lock_owner = thread->GetLockOwner();
         auto* const lock_owner = thread->GetLockOwner();
         // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
         // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance

+ 46 - 0
src/core/hle/kernel/process.cpp

@@ -142,6 +142,52 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
     return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
     return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
 }
 }
 
 
+void Process::InsertConditionVariableThread(SharedPtr<Thread> thread) {
+    auto it = cond_var_threads.begin();
+    while (it != cond_var_threads.end()) {
+        const SharedPtr<Thread> current_thread = *it;
+        if (current_thread->GetCondVarWaitAddress() < thread->GetCondVarWaitAddress()) {
+            if (current_thread->GetCondVarWaitAddress() == thread->GetCondVarWaitAddress()) {
+                if (current_thread->GetPriority() > thread->GetPriority()) {
+                    cond_var_threads.insert(it, thread);
+                    return;
+                }
+            } else {
+                cond_var_threads.insert(it, thread);
+                return;
+            }
+        }
+        ++it;
+    }
+    cond_var_threads.push_back(thread);
+}
+
+void Process::RemoveConditionVariableThread(SharedPtr<Thread> thread) {
+    auto it = cond_var_threads.begin();
+    while (it != cond_var_threads.end()) {
+        const SharedPtr<Thread> current_thread = *it;
+        if (current_thread.get() == thread.get()) {
+            cond_var_threads.erase(it);
+            return;
+        }
+        ++it;
+    }
+    UNREACHABLE();
+}
+
+std::vector<SharedPtr<Thread>> Process::GetConditionVariableThreads(const VAddr cond_var_addr) {
+    std::vector<SharedPtr<Thread>> result{};
+    auto it = cond_var_threads.begin();
+    while (it != cond_var_threads.end()) {
+        SharedPtr<Thread> current_thread = *it;
+        if (current_thread->GetCondVarWaitAddress() == cond_var_addr) {
+            result.push_back(current_thread);
+        }
+        ++it;
+    }
+    return result;
+}
+
 void Process::RegisterThread(const Thread* thread) {
 void Process::RegisterThread(const Thread* thread) {
     thread_list.push_back(thread);
     thread_list.push_back(thread);
 }
 }

+ 12 - 0
src/core/hle/kernel/process.h

@@ -232,6 +232,15 @@ public:
         return thread_list;
         return thread_list;
     }
     }
 
 
+    /// Insert a thread into the condition variable wait container
+    void InsertConditionVariableThread(SharedPtr<Thread> thread);
+
+    /// Remove a thread from the condition variable wait container
+    void RemoveConditionVariableThread(SharedPtr<Thread> thread);
+
+    /// Obtain all condition variable threads waiting for some address
+    std::vector<SharedPtr<Thread>> GetConditionVariableThreads(VAddr cond_var_addr);
+
     /// Registers a thread as being created under this process,
     /// Registers a thread as being created under this process,
     /// adding it to this process' thread list.
     /// adding it to this process' thread list.
     void RegisterThread(const Thread* thread);
     void RegisterThread(const Thread* thread);
@@ -375,6 +384,9 @@ private:
     /// List of threads that are running with this process as their owner.
     /// List of threads that are running with this process as their owner.
     std::list<const Thread*> thread_list;
     std::list<const Thread*> thread_list;
 
 
+    /// List of threads waiting for a condition variable
+    std::list<SharedPtr<Thread>> cond_var_threads;
+
     /// System context
     /// System context
     Core::System& system;
     Core::System& system;
 
 

+ 5 - 15
src/core/hle/kernel/svc.cpp

@@ -1626,6 +1626,7 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
     current_thread->SetWaitHandle(thread_handle);
     current_thread->SetWaitHandle(thread_handle);
     current_thread->SetStatus(ThreadStatus::WaitCondVar);
     current_thread->SetStatus(ThreadStatus::WaitCondVar);
     current_thread->InvalidateWakeupCallback();
     current_thread->InvalidateWakeupCallback();
+    current_process->InsertConditionVariableThread(current_thread);
 
 
     current_thread->WakeAfterDelay(nano_seconds);
     current_thread->WakeAfterDelay(nano_seconds);
 
 
@@ -1644,21 +1645,9 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
     ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
     ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
 
 
     // Retrieve a list of all threads that are waiting for this condition variable.
     // Retrieve a list of all threads that are waiting for this condition variable.
-    std::vector<SharedPtr<Thread>> waiting_threads;
-    const auto& scheduler = system.GlobalScheduler();
-    const auto& thread_list = scheduler.GetThreadList();
-
-    for (const auto& thread : thread_list) {
-        if (thread->GetCondVarWaitAddress() == condition_variable_addr) {
-            waiting_threads.push_back(thread);
-        }
-    }
-
-    // Sort them by priority, such that the highest priority ones come first.
-    std::sort(waiting_threads.begin(), waiting_threads.end(),
-              [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
-                  return lhs->GetPriority() < rhs->GetPriority();
-              });
+    auto* const current_process = system.Kernel().CurrentProcess();
+    std::vector<SharedPtr<Thread>> waiting_threads =
+        current_process->GetConditionVariableThreads(condition_variable_addr);
 
 
     // Only process up to 'target' threads, unless 'target' is -1, in which case process
     // Only process up to 'target' threads, unless 'target' is -1, in which case process
     // them all.
     // them all.
@@ -1677,6 +1666,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
 
 
         // liberate Cond Var Thread.
         // liberate Cond Var Thread.
         thread->SetCondVarWaitAddress(0);
         thread->SetCondVarWaitAddress(0);
+        current_process->RemoveConditionVariableThread(thread);
 
 
         const std::size_t current_core = system.CurrentCoreIndex();
         const std::size_t current_core = system.CurrentCoreIndex();
         auto& monitor = system.Monitor();
         auto& monitor = system.Monitor();

+ 8 - 0
src/core/hle/kernel/thread.cpp

@@ -306,8 +306,16 @@ void Thread::UpdatePriority() {
         return;
         return;
     }
     }
 
 
+    if (GetStatus() == ThreadStatus::WaitCondVar) {
+        owner_process->RemoveConditionVariableThread(this);
+    }
+
     SetCurrentPriority(new_priority);
     SetCurrentPriority(new_priority);
 
 
+    if (GetStatus() == ThreadStatus::WaitCondVar) {
+        owner_process->InsertConditionVariableThread(this);
+    }
+
     if (!lock_owner) {
     if (!lock_owner) {
         return;
         return;
     }
     }