mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-10-30 15:39:02 -05:00 
			
		
		
		
	Merge pull request #3114 from FernandoS27/cond-var
Kernel: Correct behavior of Condition Variables to be more similar to real hardware.
This commit is contained in:
		| @@ -64,8 +64,11 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ | ||||
|     } else if (thread->GetStatus() == ThreadStatus::WaitMutex || | ||||
|                thread->GetStatus() == ThreadStatus::WaitCondVar) { | ||||
|         thread->SetMutexWaitAddress(0); | ||||
|         thread->SetCondVarWaitAddress(0); | ||||
|         thread->SetWaitHandle(0); | ||||
|         if (thread->GetStatus() == ThreadStatus::WaitCondVar) { | ||||
|             thread->GetOwnerProcess()->RemoveConditionVariableThread(thread); | ||||
|             thread->SetCondVarWaitAddress(0); | ||||
|         } | ||||
|  | ||||
|         auto* const lock_owner = thread->GetLockOwner(); | ||||
|         // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance | ||||
|   | ||||
| @@ -142,6 +142,48 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { | ||||
|     return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); | ||||
| } | ||||
|  | ||||
| void Process::InsertConditionVariableThread(SharedPtr<Thread> thread) { | ||||
|     VAddr cond_var_addr = thread->GetCondVarWaitAddress(); | ||||
|     std::list<SharedPtr<Thread>>& thread_list = cond_var_threads[cond_var_addr]; | ||||
|     auto it = thread_list.begin(); | ||||
|     while (it != thread_list.end()) { | ||||
|         const SharedPtr<Thread> current_thread = *it; | ||||
|         if (current_thread->GetPriority() > thread->GetPriority()) { | ||||
|             thread_list.insert(it, thread); | ||||
|             return; | ||||
|         } | ||||
|         ++it; | ||||
|     } | ||||
|     thread_list.push_back(thread); | ||||
| } | ||||
|  | ||||
| void Process::RemoveConditionVariableThread(SharedPtr<Thread> thread) { | ||||
|     VAddr cond_var_addr = thread->GetCondVarWaitAddress(); | ||||
|     std::list<SharedPtr<Thread>>& thread_list = cond_var_threads[cond_var_addr]; | ||||
|     auto it = thread_list.begin(); | ||||
|     while (it != thread_list.end()) { | ||||
|         const SharedPtr<Thread> current_thread = *it; | ||||
|         if (current_thread.get() == thread.get()) { | ||||
|             thread_list.erase(it); | ||||
|             return; | ||||
|         } | ||||
|         ++it; | ||||
|     } | ||||
|     UNREACHABLE(); | ||||
| } | ||||
|  | ||||
| std::vector<SharedPtr<Thread>> Process::GetConditionVariableThreads(const VAddr cond_var_addr) { | ||||
|     std::vector<SharedPtr<Thread>> result{}; | ||||
|     std::list<SharedPtr<Thread>>& thread_list = cond_var_threads[cond_var_addr]; | ||||
|     auto it = thread_list.begin(); | ||||
|     while (it != thread_list.end()) { | ||||
|         SharedPtr<Thread> current_thread = *it; | ||||
|         result.push_back(current_thread); | ||||
|         ++it; | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| void Process::RegisterThread(const Thread* thread) { | ||||
|     thread_list.push_back(thread); | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #include <cstddef> | ||||
| #include <list> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/address_arbiter.h" | ||||
| @@ -232,6 +233,15 @@ public: | ||||
|         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, | ||||
|     /// adding it to this process' thread list. | ||||
|     void RegisterThread(const Thread* thread); | ||||
| @@ -375,6 +385,9 @@ private: | ||||
|     /// List of threads that are running with this process as their owner. | ||||
|     std::list<const Thread*> thread_list; | ||||
|  | ||||
|     /// List of threads waiting for a condition variable | ||||
|     std::unordered_map<VAddr, std::list<SharedPtr<Thread>>> cond_var_threads; | ||||
|  | ||||
|     /// System context | ||||
|     Core::System& system; | ||||
|  | ||||
|   | ||||
| @@ -1631,6 +1631,7 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add | ||||
|     current_thread->SetWaitHandle(thread_handle); | ||||
|     current_thread->SetStatus(ThreadStatus::WaitCondVar); | ||||
|     current_thread->InvalidateWakeupCallback(); | ||||
|     current_process->InsertConditionVariableThread(current_thread); | ||||
|  | ||||
|     current_thread->WakeAfterDelay(nano_seconds); | ||||
|  | ||||
| @@ -1649,38 +1650,23 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var | ||||
|     ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); | ||||
|  | ||||
|     // 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(); | ||||
|     auto* const current_process = system.Kernel().CurrentProcess(); | ||||
|     std::vector<SharedPtr<Thread>> waiting_threads = | ||||
|         current_process->GetConditionVariableThreads(condition_variable_addr); | ||||
|  | ||||
|     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(); | ||||
|               }); | ||||
|  | ||||
|     // Only process up to 'target' threads, unless 'target' is -1, in which case process | ||||
|     // Only process up to 'target' threads, unless 'target' is less equal 0, in which case process | ||||
|     // them all. | ||||
|     std::size_t last = waiting_threads.size(); | ||||
|     if (target != -1) | ||||
|     if (target > 0) | ||||
|         last = std::min(waiting_threads.size(), static_cast<std::size_t>(target)); | ||||
|  | ||||
|     // If there are no threads waiting on this condition variable, just exit | ||||
|     if (last == 0) | ||||
|         return RESULT_SUCCESS; | ||||
|  | ||||
|     for (std::size_t index = 0; index < last; ++index) { | ||||
|         auto& thread = waiting_threads[index]; | ||||
|  | ||||
|         ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); | ||||
|  | ||||
|         // liberate Cond Var Thread. | ||||
|         current_process->RemoveConditionVariableThread(thread); | ||||
|         thread->SetCondVarWaitAddress(0); | ||||
|  | ||||
|         const std::size_t current_core = system.CurrentCoreIndex(); | ||||
|   | ||||
| @@ -309,8 +309,16 @@ void Thread::UpdatePriority() { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (GetStatus() == ThreadStatus::WaitCondVar) { | ||||
|         owner_process->RemoveConditionVariableThread(this); | ||||
|     } | ||||
|  | ||||
|     SetCurrentPriority(new_priority); | ||||
|  | ||||
|     if (GetStatus() == ThreadStatus::WaitCondVar) { | ||||
|         owner_process->InsertConditionVariableThread(this); | ||||
|     } | ||||
|  | ||||
|     if (!lock_owner) { | ||||
|         return; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bunnei
					bunnei