diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 5128a64c..da936f8e 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -171,7 +171,7 @@
-
+
diff --git a/app/src/main/cpp/skyline/kernel/scheduler.cpp b/app/src/main/cpp/skyline/kernel/scheduler.cpp
index 9a182335..54d73ff7 100644
--- a/app/src/main/cpp/skyline/kernel/scheduler.cpp
+++ b/app/src/main/cpp/skyline/kernel/scheduler.cpp
@@ -61,8 +61,9 @@ namespace skyline::kernel {
if (optimalCore != currentCore) {
if (!alwaysInsert && thread == state.thread)
RemoveThread();
- else if (!alwaysInsert && thread != state.thread) [[unlikely]]
- throw exception("Migrating an external thread (T{}) without 'alwaysInsert' isn't supported", thread->id);
+ else if (!alwaysInsert && thread != state.thread)
+ [[unlikely]]
+ throw exception("Migrating an external thread (T{}) without 'alwaysInsert' isn't supported", thread->id);
thread->coreId = optimalCore->id;
InsertThread(thread);
state.logger->Debug("Load Balancing T{}: C{} -> C{}", thread->id, currentCore->id, optimalCore->id);
@@ -121,12 +122,26 @@ namespace skyline::kernel {
void Scheduler::WaitSchedule(bool loadBalance) {
auto &thread{state.thread};
- auto *core{&cores.at(thread->coreId)};
-
+ CoreContext *core{&cores.at(thread->coreId)};
std::unique_lock lock(core->mutex);
+
+ auto wakeFunction{[&]() {
+ if (!thread->affinityMask.test(thread->coreId)) [[unlikely]] {
+ lock.unlock();
+
+ RemoveThread();
+ thread->coreId = thread->idealCore;
+ InsertThread(thread);
+
+ core = &cores.at(thread->coreId);
+ lock = std::unique_lock(core->mutex);
+ }
+ return !core->queue.empty() && core->queue.front() == thread;
+ }};
+
if (loadBalance && thread->affinityMask.count() > 1) {
std::chrono::milliseconds loadBalanceThreshold{PreemptiveTimeslice * 2}; //!< The amount of time that needs to pass unscheduled for a thread to attempt load balancing
- while (!thread->wakeCondition.wait_for(lock, loadBalanceThreshold, [&]() { return !core->queue.empty() && core->queue.front() == thread; })) {
+ while (!thread->wakeCondition.wait_for(lock, loadBalanceThreshold, wakeFunction)) {
lock.unlock();
LoadBalance(state.thread);
if (thread->coreId == core->id) {
@@ -139,7 +154,7 @@ namespace skyline::kernel {
loadBalanceThreshold *= 2; // We double the duration required for future load balancing for this invocation to minimize pointless load balancing
}
} else {
- thread->wakeCondition.wait(lock, [&]() { return !core->queue.empty() && core->queue.front() == thread; });
+ thread->wakeCondition.wait(lock, wakeFunction);
}
if (thread->priority == core->preemptionPriority) {
@@ -156,7 +171,19 @@ namespace skyline::kernel {
auto *core{&cores.at(thread->coreId)};
std::unique_lock lock(core->mutex);
- if (thread->wakeCondition.wait_for(lock, timeout, [&]() { return !core->queue.empty() && core->queue.front() == thread; })) {
+ if (thread->wakeCondition.wait_for(lock, timeout, [&]() {
+ if (!thread->affinityMask.test(thread->coreId)) [[unlikely]] {
+ lock.unlock();
+
+ RemoveThread();
+ thread->coreId = thread->idealCore;
+ InsertThread(thread);
+
+ core = &cores.at(thread->coreId);
+ lock = std::unique_lock(core->mutex);
+ }
+ return !core->queue.empty() && core->queue.front() == thread;
+ })) {
if (thread->priority == core->preemptionPriority) {
struct itimerspec spec{.it_value = {.tv_nsec = std::chrono::duration_cast(PreemptiveTimeslice).count()}};
timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
@@ -182,11 +209,12 @@ namespace skyline::kernel {
// Splice the linked element from the beginning of the queue to where it's priority is present
core.queue.splice(std::upper_bound(core.queue.begin(), core.queue.end(), thread->priority.load(), type::KThread::IsHigherPriority), core.queue, core.queue.begin());
- auto& front{core.queue.front()};
+ auto &front{core.queue.front()};
if (front != thread)
front->wakeCondition.notify_one(); // If we aren't at the front of the queue, only then should we wake the thread at the front up
- } else if (!thread->forceYield) { [[unlikely]]
- throw exception("T{} called Rotate while not being in C{}'s queue", thread->id, thread->coreId);
+ } else if (!thread->forceYield) {
+ [[unlikely]]
+ throw exception("T{} called Rotate while not being in C{}'s queue", thread->id, thread->coreId);
}
thread->averageTimeslice = (thread->averageTimeslice / 4) + (3 * (util::GetTimeTicks() - thread->timesliceStart / 4));
@@ -202,6 +230,34 @@ namespace skyline::kernel {
thread->forceYield = false;
}
+ void Scheduler::RemoveThread() {
+ auto &thread{state.thread};
+ auto &core{cores.at(thread->coreId)};
+ {
+ std::unique_lock lock(core.mutex);
+ auto it{std::find(core.queue.begin(), core.queue.end(), thread)};
+ if (it != core.queue.end()) {
+ it = core.queue.erase(it);
+ if (it == core.queue.begin()) {
+ // We need to update the averageTimeslice accordingly, if we've been unscheduled by this
+ if (thread->timesliceStart)
+ thread->averageTimeslice = (thread->averageTimeslice / 4) + (3 * (util::GetTimeTicks() - thread->timesliceStart / 4));
+
+ if (it != core.queue.end())
+ (*it)->wakeCondition.notify_one(); // We need to wake the thread at the front of the queue, if we were at the front previously
+ }
+ }
+ }
+
+ if (thread->isPreempted) {
+ struct itimerspec spec{};
+ timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
+ thread->isPreempted = false;
+ }
+
+ YieldPending = false;
+ }
+
void Scheduler::UpdatePriority(const std::shared_ptr &thread) {
std::lock_guard migrationLock(thread->coreMigrationMutex);
auto *core{&cores.at(thread->coreId)};
@@ -254,6 +310,15 @@ namespace skyline::kernel {
}
}
+ void Scheduler::UpdateCore(const std::shared_ptr &thread) {
+ auto *core{&cores.at(thread->coreId)};
+ std::unique_lock coreLock(core->mutex);
+ if (core->queue.front() == thread)
+ thread->SendSignal(YieldSignal);
+ else
+ thread->wakeCondition.notify_one();
+ }
+
void Scheduler::ParkThread() {
auto &thread{state.thread};
std::lock_guard migrationLock(thread->coreMigrationMutex);
@@ -293,32 +358,4 @@ namespace skyline::kernel {
}
}
}
-
- void Scheduler::RemoveThread() {
- auto &thread{state.thread};
- auto &core{cores.at(thread->coreId)};
- {
- std::unique_lock lock(core.mutex);
- auto it{std::find(core.queue.begin(), core.queue.end(), thread)};
- if (it != core.queue.end()) {
- it = core.queue.erase(it);
- if (it == core.queue.begin()) {
- // We need to update the averageTimeslice accordingly, if we've been unscheduled by this
- if (thread->timesliceStart)
- thread->averageTimeslice = (thread->averageTimeslice / 4) + (3 * (util::GetTimeTicks() - thread->timesliceStart / 4));
-
- if (it != core.queue.end())
- (*it)->wakeCondition.notify_one(); // We need to wake the thread at the front of the queue, if we were at the front previously
- }
- }
- }
-
- if (thread->isPreempted) {
- struct itimerspec spec{};
- timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
- thread->isPreempted = false;
- }
-
- YieldPending = false;
- }
}
diff --git a/app/src/main/cpp/skyline/kernel/scheduler.h b/app/src/main/cpp/skyline/kernel/scheduler.h
index 1669f065..81d95203 100644
--- a/app/src/main/cpp/skyline/kernel/scheduler.h
+++ b/app/src/main/cpp/skyline/kernel/scheduler.h
@@ -103,11 +103,22 @@ namespace skyline {
*/
void Rotate(bool cooperative = true);
+ /**
+ * @brief Removes the calling thread from it's resident core queue
+ */
+ void RemoveThread();
+
/**
* @brief Updates the placement of the supplied thread in it's resident core's queue according to it's new priority
*/
void UpdatePriority(const std::shared_ptr& thread);
+ /**
+ * @brief Updates the core that the supplied thread is resident to according to it's new affinity mask and ideal core
+ * @note This supports changing the core of a thread which is currently running
+ */
+ void UpdateCore(const std::shared_ptr& thread);
+
/**
* @brief Parks the calling thread after removing it from it's resident core's queue and inserts it on the core it's been awoken on
* @note This will not handle waiting for the thread to be scheduled, this should be followed with a call to WaitSchedule/TimedWaitSchedule
@@ -119,11 +130,6 @@ namespace skyline {
* @note We will only wake a thread if it is determined to be a better pick than the thread which would be run on this core next
*/
void WakeParkedThread();
-
- /**
- * @brief Removes the calling thread from it's resident core queue
- */
- void RemoveThread();
};
/**
diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp
index 559286a4..764fbf5e 100644
--- a/app/src/main/cpp/skyline/kernel/svc.cpp
+++ b/app/src/main/cpp/skyline/kernel/svc.cpp
@@ -412,10 +412,11 @@ namespace skyline::kernel::svc {
state.logger->Debug("svcSetThreadCoreMask: Setting thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask);
+ std::lock_guard guard(thread->coreMigrationMutex);
thread->idealCore = idealCore;
thread->affinityMask = affinityMask;
- if (!affinityMask.test(thread->coreId)) {
+ if (!affinityMask.test(thread->coreId) && thread->coreId != constant::ParkedCoreId) {
state.logger->Debug("svcSetThreadCoreMask: Migrating thread #{} to Ideal Core C{} -> C{}", thread->id, thread->coreId, idealCore);
if (thread == state.thread) {
@@ -425,7 +426,7 @@ namespace skyline::kernel::svc {
} else if (!thread->running) {
thread->coreId = idealCore;
} else {
- throw exception("svcSetThreadCoreMask: Migrating a running thread due to a new core mask is not supported");
+ state.scheduler->UpdateCore(thread);
}
}
diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.h b/app/src/main/cpp/skyline/kernel/types/KThread.h
index e9f6f14f..87836961 100644
--- a/app/src/main/cpp/skyline/kernel/types/KThread.h
+++ b/app/src/main/cpp/skyline/kernel/types/KThread.h
@@ -41,7 +41,7 @@ namespace skyline {
u64 entryArgument;
void *stackTop;
- std::condition_variable_any wakeCondition; //!< A conditional variable which is signalled to wake the current thread while it's sleeping
+ std::condition_variable wakeCondition; //!< A conditional variable which is signalled to wake the current thread while it's sleeping
std::atomic basePriority; //!< The priority of the thread for the scheduler without any priority-inheritance
std::atomic priority; //!< The priority of the thread for the scheduler
i8 idealCore; //!< The ideal CPU core for this thread to run on