mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
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:
parent
34f8881d3e
commit
22c6b9fab2
3 changed files with 67 additions and 24 deletions
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue