mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-29 15:25:28 +03:00
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:
parent
8b973a3de3
commit
2525bafe06
@ -85,6 +85,22 @@ namespace skyline::kernel {
|
|||||||
return *currentCore;
|
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) {
|
void Scheduler::InsertThread(const std::shared_ptr<type::KThread> &thread) {
|
||||||
std::scoped_lock migrationLock{thread->coreMigrationMutex};
|
std::scoped_lock migrationLock{thread->coreMigrationMutex};
|
||||||
auto &core{cores.at(thread->coreId)};
|
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.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);
|
core.queue.push_front(thread);
|
||||||
|
|
||||||
if (state.thread != front) {
|
YieldThread(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;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
core.queue.push_front(thread);
|
core.queue.push_front(thread);
|
||||||
}
|
}
|
||||||
@ -287,10 +291,7 @@ namespace skyline::kernel {
|
|||||||
} else if (currentIt == core->queue.begin()) {
|
} 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
|
// 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 (nextIt != core->queue.end() && (*nextIt)->priority < thread->priority) {
|
||||||
if (!thread->pendingYield) {
|
YieldThread(thread);
|
||||||
thread->SendSignal(YieldSignal);
|
|
||||||
thread->pendingYield = true;
|
|
||||||
}
|
|
||||||
} else if (!thread->isPreempted && thread->priority == core->preemptionPriority) {
|
} 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
|
// If the thread needs to be preempted due to its new priority then arm its preemption timer
|
||||||
thread->ArmPreemptionTimer(PreemptiveTimeslice);
|
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)};
|
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()) {
|
if (targetIt == core->queue.begin() && targetIt != core->queue.end()) {
|
||||||
core->queue.insert(std::next(core->queue.begin()), thread);
|
core->queue.insert(std::next(core->queue.begin()), thread);
|
||||||
auto front{core->queue.front()};
|
YieldThread(core->queue.front());
|
||||||
if (!front->pendingYield) {
|
|
||||||
front->SendSignal(YieldSignal);
|
|
||||||
front->pendingYield = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
core->queue.insert(targetIt, thread);
|
core->queue.insert(targetIt, thread);
|
||||||
}
|
}
|
||||||
@ -381,10 +378,7 @@ namespace skyline::kernel {
|
|||||||
|
|
||||||
if (it == core->queue.begin()) {
|
if (it == core->queue.begin()) {
|
||||||
// We need to send a yield signal to the thread if it's currently running
|
// We need to send a yield signal to the thread if it's currently running
|
||||||
if (!thread->pendingYield) {
|
YieldThread(thread);
|
||||||
thread->SendSignal(YieldSignal);
|
|
||||||
thread->pendingYield = true;
|
|
||||||
}
|
|
||||||
thread->forceYield = true;
|
thread->forceYield = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,6 +64,11 @@ namespace skyline {
|
|||||||
*/
|
*/
|
||||||
void MigrateToCore(const std::shared_ptr<type::KThread> &thread, CoreContext *¤tCore, CoreContext *targetCore, std::unique_lock<std::mutex> &lock);
|
void MigrateToCore(const std::shared_ptr<type::KThread> &thread, CoreContext *¤tCore, 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:
|
public:
|
||||||
static constexpr std::chrono::milliseconds PreemptiveTimeslice{10}; //!< The duration of time a preemptive thread can run before yielding
|
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
|
inline static int YieldSignal{SIGRTMIN}; //!< The signal used to cause a non-cooperative yield in running threads
|
||||||
|
Loading…
Reference in New Issue
Block a user