diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index ef2d25b4..dfe4c22f 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 54d73ff7..b5494971 100644
--- a/app/src/main/cpp/skyline/kernel/scheduler.cpp
+++ b/app/src/main/cpp/skyline/kernel/scheduler.cpp
@@ -120,6 +120,27 @@ namespace skyline::kernel {
}
}
+ void Scheduler::MigrateToIdealCore(const std::shared_ptr &thread, CoreContext *&core, std::unique_lock &lock) {
+ // We need to check if the thread was in it's resident core's queue
+ // If it was, we need to remove it from the queue
+ auto it{std::find(core->queue.begin(), core->queue.end(), thread)};
+ bool wasInserted{it != core->queue.end()};
+ if (wasInserted) {
+ it = core->queue.erase(it);
+ if (it == core->queue.begin() && it != core->queue.end())
+ (*it)->wakeCondition.notify_one();
+ }
+ lock.unlock();
+
+ thread->coreId = thread->idealCore;
+ if (wasInserted)
+ // We need to add the thread to the ideal core queue, if it was previously it's resident core's queue
+ InsertThread(thread);
+
+ core = &cores.at(thread->coreId);
+ lock = std::unique_lock(core->mutex);
+ }
+
void Scheduler::WaitSchedule(bool loadBalance) {
auto &thread{state.thread};
CoreContext *core{&cores.at(thread->coreId)};
@@ -127,14 +148,7 @@ namespace skyline::kernel {
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);
+ MigrateToIdealCore(thread, core, lock);
}
return !core->queue.empty() && core->queue.front() == thread;
}};
@@ -173,14 +187,7 @@ namespace skyline::kernel {
std::unique_lock lock(core->mutex);
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);
+ MigrateToIdealCore(thread, core, lock);
}
return !core->queue.empty() && core->queue.front() == thread;
})) {
@@ -212,9 +219,8 @@ namespace skyline::kernel {
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));
diff --git a/app/src/main/cpp/skyline/kernel/scheduler.h b/app/src/main/cpp/skyline/kernel/scheduler.h
index 81d95203..f355b5c9 100644
--- a/app/src/main/cpp/skyline/kernel/scheduler.h
+++ b/app/src/main/cpp/skyline/kernel/scheduler.h
@@ -57,6 +57,12 @@ namespace skyline {
std::mutex parkedMutex; //!< Synchronizes all operations on the queue of parked threads
std::list> parkedQueue; //!< A queue of threads which are parked and waiting on core migration
+ /**
+ * @brief Migrate a thread from it's resident core to it's ideal core
+ * @note This is used to handle non-cooperative core affinity mask changes where the resident core is not in it's new affinity mask
+ */
+ void MigrateToIdealCore(const std::shared_ptr& thread, CoreContext*& core, std::unique_lock& lock);
+
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 yield in running threads
diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp
index 10eff2b6..8b207b4c 100644
--- a/app/src/main/cpp/skyline/kernel/svc.cpp
+++ b/app/src/main/cpp/skyline/kernel/svc.cpp
@@ -300,12 +300,12 @@ namespace skyline::kernel::svc {
} else {
switch (in) {
case yieldWithCoreMigration:
- state.logger->Debug("svcSleepThread: Waking any appropriate parked threads");
+ state.logger->Debug("svcSleepThread: Waking any appropriate parked threads and yielding");
state.scheduler->WakeParkedThread();
- break;
-
+ [[fallthrough]];
case yieldWithoutCoreMigration:
- state.logger->Debug("svcSleepThread: Cooperative Yield");
+ if (in == yieldWithoutCoreMigration)
+ state.logger->Debug("svcSleepThread: Cooperative yield");
state.scheduler->Rotate();
state.scheduler->WaitSchedule();
break;
@@ -710,7 +710,7 @@ namespace skyline::kernel::svc {
else if (result == result::InvalidCurrentMemory)
result = Result{}; // If the mutex value isn't expected then it's still successful
else if (result == result::InvalidHandle)
- state.logger->Warn("svcArbitrateLock: 'ownerHandle' invalid: 0x{:X}", ownerHandle);
+ state.logger->Warn("svcArbitrateLock: 'ownerHandle' invalid: 0x{:X} (0x{:X})", ownerHandle, mutex);
state.ctx->gpr.w0 = result;
}
diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp
index 41097463..d731047a 100644
--- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp
+++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp
@@ -264,16 +264,14 @@ namespace skyline::kernel::type {
state.scheduler->WaitSchedule(false);
}
- while (true) {
- KHandle value{};
- if (__atomic_compare_exchange_n(mutex, &value, tag, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
- return {};
- if (!(value & HandleWaitersBit))
- if (!__atomic_compare_exchange_n(mutex, &value, value | HandleWaitersBit, false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED))
- continue;
- if (MutexLock(mutex, value & ~HandleWaitersBit, tag) == Result{})
- return {};
- }
+ KHandle value{};
+ if (!__atomic_compare_exchange_n(mutex, &value, tag, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+ while (MutexLock(mutex, value & ~HandleWaitersBit, tag) != Result{})
+ if ((value = __atomic_or_fetch(mutex, HandleWaitersBit, __ATOMIC_SEQ_CST)) == HandleWaitersBit)
+ if (__atomic_compare_exchange_n(mutex, &value, tag, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+ break;
+
+ return {};
}
void KProcess::ConditionalVariableSignal(u32 *key, i32 amount) {
@@ -281,7 +279,6 @@ namespace skyline::kernel::type {
auto queue{syncWaiters.equal_range(key)};
auto it{queue.first};
- if (queue.first != queue.second)
for (i32 waiterCount{amount}; it != queue.second && (amount <= 0 || waiterCount); it = syncWaiters.erase(it), waiterCount--)
state.scheduler->InsertThread(it->second);
@@ -330,9 +327,8 @@ namespace skyline::kernel::type {
return result::InvalidState;
i32 waiterCount{amount};
- if (queue.first != queue.second)
- for (auto it{queue.first}; it != queue.second && (amount <= 0 || waiterCount); it = syncWaiters.erase(it), waiterCount--)
- state.scheduler->InsertThread(it->second);
+ for (auto it{queue.first}; it != queue.second && (amount <= 0 || waiterCount); it = syncWaiters.erase(it), waiterCount--)
+ state.scheduler->InsertThread(it->second);
return {};
}