mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 10:21:51 +01: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;
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
Loading…
Reference in New Issue
Block a user