yuzu-emu
/
yuzu-mainline
Archived
1
0
Fork 0

svc: Implement yield types 0 and -1

This commit is contained in:
Zach Hilman 2018-11-18 23:44:19 -05:00
parent f02b125ac8
commit 409dcf0e0a
6 changed files with 130 additions and 2 deletions

View File

@ -6,6 +6,7 @@
#include <array> #include <array>
#include <deque> #include <deque>
#include <functional>
#include <boost/range/algorithm_ext/erase.hpp> #include <boost/range/algorithm_ext/erase.hpp>
namespace Common { namespace Common {
@ -49,6 +50,21 @@ struct ThreadQueueList {
return T(); return T();
} }
T get_first_filter(std::function<bool(T)> filter) const {
const Queue* cur = first;
while (cur != nullptr) {
if (!cur->data.empty()) {
for (const auto& item : cur->data) {
if (filter(item))
return item;
}
}
cur = cur->next_nonempty;
}
return T();
}
T pop_first() { T pop_first() {
Queue* cur = first; Queue* cur = first;
while (cur != nullptr) { while (cur != nullptr) {

View File

@ -169,6 +169,16 @@ void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
ready_queue.remove(priority, thread); ready_queue.remove(priority, thread);
} }
void Scheduler::RescheduleThread(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
// Thread is not in queue
ASSERT(ready_queue.contains(thread) != -1);
ready_queue.remove(priority, thread);
ready_queue.push_back(priority, thread);
}
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex); std::lock_guard<std::mutex> lock(scheduler_mutex);
@ -179,4 +189,12 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
ready_queue.prepare(priority); ready_queue.prepare(priority);
} }
Thread* Scheduler::GetNextSuggestedThread(u32 core) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
const auto mask = 1 << core;
return ready_queue.get_first_filter(
[&mask](Thread* thread) { return (thread->GetAffinityMask() & mask) != 0; });
}
} // namespace Kernel } // namespace Kernel

View File

@ -48,9 +48,15 @@ public:
/// Unschedules a thread that was already scheduled /// Unschedules a thread that was already scheduled
void UnscheduleThread(Thread* thread, u32 priority); void UnscheduleThread(Thread* thread, u32 priority);
/// Moves a thread to the back of the current priority queue
void RescheduleThread(Thread* thread, u32 priority);
/// Sets the priority of a thread in the scheduler /// Sets the priority of a thread in the scheduler
void SetThreadPriority(Thread* thread, u32 priority); void SetThreadPriority(Thread* thread, u32 priority);
/// Gets the next suggested thread for load balancing
Thread* GetNextSuggestedThread(u32 core);
/// Returns a list of all threads managed by the scheduler /// Returns a list of all threads managed by the scheduler
const std::vector<SharedPtr<Thread>>& GetThreadList() const { const std::vector<SharedPtr<Thread>>& GetThreadList() const {
return thread_list; return thread_list;

View File

@ -962,16 +962,39 @@ static void SleepThread(s64 nanoseconds) {
// Don't attempt to yield execution if there are no available threads to run, // Don't attempt to yield execution if there are no available threads to run,
// this way we avoid a useless reschedule to the idle thread. // this way we avoid a useless reschedule to the idle thread.
if (nanoseconds == 0 && !Core::System::GetInstance().CurrentScheduler().HaveReadyThreads()) if (!Core::System::GetInstance().CurrentScheduler().HaveReadyThreads())
return; return;
if (nanoseconds <= 0) {
switch (nanoseconds) {
case 0:
GetCurrentThread()->YieldNormal();
break;
case -1:
GetCurrentThread()->YieldWithLoadBalancing();
break;
case -2:
GetCurrentThread()->YieldAndWaitForLoadBalancing();
break;
default:
UNREACHABLE_MSG(
"Unimplemented sleep yield type '{:016X}'! Falling back to forced reschedule...",
nanoseconds);
}
nanoseconds = 0;
}
// Sleep current thread and check for next thread to schedule // Sleep current thread and check for next thread to schedule
WaitCurrentThread_Sleep(); WaitCurrentThread_Sleep();
// Create an event to wake the thread up after the specified nanosecond delay has passed // Create an event to wake the thread up after the specified nanosecond delay has passed
GetCurrentThread()->WakeAfterDelay(nanoseconds); GetCurrentThread()->WakeAfterDelay(nanoseconds);
Core::System::GetInstance().PrepareReschedule(); Core::System::GetInstance().CpuCore(0).PrepareReschedule();
Core::System::GetInstance().CpuCore(1).PrepareReschedule();
Core::System::GetInstance().CpuCore(2).PrepareReschedule();
Core::System::GetInstance().CpuCore(3).PrepareReschedule();
} }
/// Wait process wide key atomic /// Wait process wide key atomic

View File

@ -388,6 +388,66 @@ bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> t
return wakeup_callback(reason, std::move(thread), std::move(object), index); return wakeup_callback(reason, std::move(thread), std::move(object), index);
} }
void Thread::YieldNormal() {
// Avoid yielding if the thread isn't even running.
if (status != ThreadStatus::Running) {
return;
}
if (nominal_priority < THREADPRIO_COUNT) {
scheduler->RescheduleThread(this, nominal_priority);
scheduler->Reschedule();
}
}
void Thread::YieldWithLoadBalancing() {
auto priority = nominal_priority;
auto core = processor_id;
// Avoid yielding if the thread isn't even running.
if (status != ThreadStatus::Running) {
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
return;
}
SharedPtr<Thread> next;
const auto& threads = scheduler->GetThreadList();
if (priority < THREADPRIO_COUNT) {
// Reschedule thread to end of queue.
scheduler->RescheduleThread(this, priority);
const auto iter = std::find_if(threads.begin(), threads.end(),
[&priority](const SharedPtr<Thread>& thread) {
return thread->GetNominalPriority() == priority;
});
if (iter != threads.end())
next = iter->get();
}
Thread* suggested_thread = nullptr;
for (int i = 0; i < 4; ++i) {
if (i == core)
continue;
const auto res =
Core::System::GetInstance().CpuCore(i).Scheduler().GetNextSuggestedThread(core);
if (res != nullptr) {
suggested_thread = res;
break;
}
}
if (suggested_thread != nullptr)
suggested_thread->ChangeCore(core, suggested_thread->GetAffinityMask());
}
void Thread::YieldAndWaitForLoadBalancing() {
UNIMPLEMENTED_MSG("Wait for load balancing thread yield type is not implemented!");
}
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
/** /**

View File

@ -26,6 +26,7 @@ enum ThreadPriority : u32 {
THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
THREADPRIO_LOWEST = 63, ///< Lowest thread priority THREADPRIO_LOWEST = 63, ///< Lowest thread priority
THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities.
}; };
enum ThreadProcessorId : s32 { enum ThreadProcessorId : s32 {
@ -370,6 +371,10 @@ public:
return affinity_mask; return affinity_mask;
} }
void YieldNormal();
void YieldWithLoadBalancing();
void YieldAndWaitForLoadBalancing();
private: private:
explicit Thread(KernelCore& kernel); explicit Thread(KernelCore& kernel);
~Thread() override; ~Thread() override;