Consolidate thread yielding in Scheduler

There's multiple locations where a thread is yielded in the scheduler and all of them repeat the code of checking for `pendingYield` and signalling with an optional optimization of checking if the thread being yielded is the calling thread.

All this functionality has now been consolidated into `Scheduler::YieldThread` which checks for `pendingYield` and does the calling thread yield optimization. This should lead to better readability and better performance in cases where `UpdatePriority` would signal the calling thread.
This commit is contained in:
PixelyIon 2022-11-30 02:58:02 +05:30 committed by Mark Collins
parent 8b973a3de3
commit 2525bafe06
2 changed files with 25 additions and 26 deletions

View File

@ -85,6 +85,22 @@ namespace skyline::kernel {
return *currentCore;
}
void Scheduler::YieldThread(const std::shared_ptr<type::KThread> &thread) {
if (state.thread != thread) {
// If another thread is being yielded, we need to send it an OS signal to yield
if (!thread->pendingYield) {
// We only want to yield the thread if it hasn't already been sent a signal to yield in the past
// Not doing this can lead to races and deadlocks but is also slower as it prevents redundant signals
thread->SendSignal(YieldSignal);
thread->pendingYield = true;
}
} else {
// If the calling thread is being yielded, we can just set the YieldPending flag
// This avoids an OS signal which would just flip the YieldPending flag but with significantly more overhead
YieldPending = true;
}
}
void Scheduler::InsertThread(const std::shared_ptr<type::KThread> &thread) {
std::scoped_lock migrationLock{thread->coreMigrationMutex};
auto &core{cores.at(thread->coreId)};
@ -117,19 +133,7 @@ namespace skyline::kernel {
core.queue.splice(std::upper_bound(core.queue.begin(), core.queue.end(), front->priority.load(), type::KThread::IsHigherPriority), core.queue, core.queue.begin());
core.queue.push_front(thread);
if (state.thread != front) {
// If the calling thread isn't at the front, we need to send it an OS signal to yield
if (!front->pendingYield) {
// We only want to yield the thread if it hasn't already been sent a signal to yield in the past
// Not doing this can lead to races and deadlocks but is also slower as it prevents redundant signals
front->SendSignal(YieldSignal);
front->pendingYield = true;
}
} else {
// If the calling thread at the front is being yielded, we can just set the YieldPending flag
// This avoids an OS signal which would just flip the YieldPending flag but with significantly more overhead
YieldPending = true;
}
YieldThread(front);
} else {
core.queue.push_front(thread);
}
@ -287,10 +291,7 @@ namespace skyline::kernel {
} else if (currentIt == core->queue.begin()) {
// Alternatively, if it's currently running then we'd just want to yield if there's a higher priority thread to run instead
if (nextIt != core->queue.end() && (*nextIt)->priority < thread->priority) {
if (!thread->pendingYield) {
thread->SendSignal(YieldSignal);
thread->pendingYield = true;
}
YieldThread(thread);
} else if (!thread->isPreempted && thread->priority == core->preemptionPriority) {
// If the thread needs to be preempted due to its new priority then arm its preemption timer
thread->ArmPreemptionTimer(PreemptiveTimeslice);
@ -305,11 +306,7 @@ namespace skyline::kernel {
auto targetIt{std::upper_bound(core->queue.begin(), core->queue.end(), thread->priority.load(), type::KThread::IsHigherPriority)};
if (targetIt == core->queue.begin() && targetIt != core->queue.end()) {
core->queue.insert(std::next(core->queue.begin()), thread);
auto front{core->queue.front()};
if (!front->pendingYield) {
front->SendSignal(YieldSignal);
front->pendingYield = true;
}
YieldThread(core->queue.front());
} else {
core->queue.insert(targetIt, thread);
}
@ -381,10 +378,7 @@ namespace skyline::kernel {
if (it == core->queue.begin()) {
// We need to send a yield signal to the thread if it's currently running
if (!thread->pendingYield) {
thread->SendSignal(YieldSignal);
thread->pendingYield = true;
}
YieldThread(thread);
thread->forceYield = true;
}
} else {

View File

@ -64,6 +64,11 @@ namespace skyline {
*/
void MigrateToCore(const std::shared_ptr<type::KThread> &thread, CoreContext *&currentCore, CoreContext *targetCore, std::unique_lock<std::mutex> &lock);
/**
* @brief Trigger a thread to yield via a signal or on SVC exit if it is the current thread
*/
void YieldThread(const std::shared_ptr<type::KThread> &thread);
public:
static constexpr std::chrono::milliseconds PreemptiveTimeslice{10}; //!< The duration of time a preemptive thread can run before yielding
inline static int YieldSignal{SIGRTMIN}; //!< The signal used to cause a non-cooperative yield in running threads