Kernel: Correct behavior of Address Arbiter threads. (#3165)

* Kernel: Correct behavior of Address Arbiter threads.

This corrects arbitration threads to behave just like in Horizon OS.
They are added into a container and released according to what priority
they had when added. Horizon OS does not reorder them if their priority
changes.

* Kernel: Address Feedback.
This commit is contained in:
Fernando Sahmkow 2019-12-11 11:55:38 -04:00 committed by bunnei
parent 34f8881d3e
commit 22c6b9fab2
3 changed files with 67 additions and 24 deletions

View file

@ -17,10 +17,10 @@
#include "core/memory.h" #include "core/memory.h"
namespace Kernel { namespace Kernel {
namespace {
// Wake up num_to_wake (or all) threads in a vector. // Wake up num_to_wake (or all) threads in a vector.
void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake) { void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
auto& system = Core::System::GetInstance(); s32 num_to_wake) {
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
// them all. // them all.
std::size_t last = waiting_threads.size(); std::size_t last = waiting_threads.size();
@ -32,12 +32,12 @@ void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s3
for (std::size_t i = 0; i < last; i++) { for (std::size_t i = 0; i < last; i++) {
ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb); ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
RemoveThread(waiting_threads[i]);
waiting_threads[i]->SetArbiterWaitAddress(0); waiting_threads[i]->SetArbiterWaitAddress(0);
waiting_threads[i]->ResumeFromWait(); waiting_threads[i]->ResumeFromWait();
system.PrepareReschedule(waiting_threads[i]->GetProcessorID()); system.PrepareReschedule(waiting_threads[i]->GetProcessorID());
} }
} }
} // Anonymous namespace
AddressArbiter::AddressArbiter(Core::System& system) : system{system} {} AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
AddressArbiter::~AddressArbiter() = default; AddressArbiter::~AddressArbiter() = default;
@ -184,6 +184,7 @@ ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 t
ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) { ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
current_thread->SetArbiterWaitAddress(address); current_thread->SetArbiterWaitAddress(address);
InsertThread(SharedFrom(current_thread));
current_thread->SetStatus(ThreadStatus::WaitArb); current_thread->SetStatus(ThreadStatus::WaitArb);
current_thread->InvalidateWakeupCallback(); current_thread->InvalidateWakeupCallback();
current_thread->WakeAfterDelay(timeout); current_thread->WakeAfterDelay(timeout);
@ -192,26 +193,51 @@ ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
return RESULT_TIMEOUT; return RESULT_TIMEOUT;
} }
std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress( void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) {
VAddr address) const { ASSERT(thread->GetStatus() == ThreadStatus::WaitArb);
RemoveThread(thread);
thread->SetArbiterWaitAddress(0);
}
// Retrieve all threads that are waiting for this address. void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) {
std::vector<std::shared_ptr<Thread>> threads; const VAddr arb_addr = thread->GetArbiterWaitAddress();
const auto& scheduler = system.GlobalScheduler(); std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
const auto& thread_list = scheduler.GetThreadList(); auto it = thread_list.begin();
while (it != thread_list.end()) {
for (const auto& thread : thread_list) { const std::shared_ptr<Thread>& current_thread = *it;
if (thread->GetArbiterWaitAddress() == address) { if (current_thread->GetPriority() >= thread->GetPriority()) {
threads.push_back(thread); thread_list.insert(it, thread);
return;
} }
++it;
} }
thread_list.push_back(std::move(thread));
}
// Sort them by priority, such that the highest priority ones come first. void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
std::sort(threads.begin(), threads.end(), const VAddr arb_addr = thread->GetArbiterWaitAddress();
[](const std::shared_ptr<Thread>& lhs, const std::shared_ptr<Thread>& rhs) { std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
return lhs->GetPriority() < rhs->GetPriority(); auto it = thread_list.begin();
}); while (it != thread_list.end()) {
const std::shared_ptr<Thread>& current_thread = *it;
if (current_thread.get() == thread.get()) {
thread_list.erase(it);
return;
}
++it;
}
UNREACHABLE();
}
return threads; std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) {
std::vector<std::shared_ptr<Thread>> result;
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[address];
auto it = thread_list.begin();
while (it != thread_list.end()) {
std::shared_ptr<Thread> current_thread = *it;
result.push_back(std::move(current_thread));
++it;
}
return result;
} }
} // namespace Kernel } // namespace Kernel

View file

@ -4,7 +4,9 @@
#pragma once #pragma once
#include <list>
#include <memory> #include <memory>
#include <unordered_map>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
@ -48,6 +50,9 @@ public:
/// Waits on an address with a particular arbitration type. /// Waits on an address with a particular arbitration type.
ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns); ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
/// Removes a thread from the container and resets its address arbiter adress to 0
void HandleWakeupThread(std::shared_ptr<Thread> thread);
private: private:
/// Signals an address being waited on. /// Signals an address being waited on.
ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake); ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
@ -71,8 +76,20 @@ private:
// Waits on the given address with a timeout in nanoseconds // Waits on the given address with a timeout in nanoseconds
ResultCode WaitForAddressImpl(VAddr address, s64 timeout); ResultCode WaitForAddressImpl(VAddr address, s64 timeout);
/// Wake up num_to_wake (or all) threads in a vector.
void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake);
/// Insert a thread into the address arbiter container
void InsertThread(std::shared_ptr<Thread> thread);
/// Removes a thread from the address arbiter container
void RemoveThread(std::shared_ptr<Thread> thread);
// Gets the threads waiting on an address. // Gets the threads waiting on an address.
std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const; std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address);
/// List of threads waiting for a address arbiter
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads;
Core::System& system; Core::System& system;
}; };

View file

@ -78,9 +78,9 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
} }
} }
if (thread->GetArbiterWaitAddress() != 0) { if (thread->GetStatus() == ThreadStatus::WaitArb) {
ASSERT(thread->GetStatus() == ThreadStatus::WaitArb); auto& address_arbiter = thread->GetOwnerProcess()->GetAddressArbiter();
thread->SetArbiterWaitAddress(0); address_arbiter.HandleWakeupThread(thread);
} }
if (resume) { if (resume) {