svc: Implement yield types 0 and -1
This commit is contained in:
parent
f02b125ac8
commit
409dcf0e0a
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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!");
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue