From 1ee9d5c78c1c5216c92764977363fad38b0d4f0b Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 27 May 2024 01:24:24 +0200 Subject: [PATCH] coreinit: Tweak JD2019 workaround to avoid XCX softlock --- src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp | 2 +- src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 8 ++++---- src/Cafe/OS/libs/coreinit/coreinit_Thread.h | 6 +++--- src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp | 9 +++++---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp index 9e5de19e9..c144c3841 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp @@ -310,7 +310,7 @@ namespace coreinit currentThread->mutexQueue.removeMutex(mutex); mutex->owner = nullptr; if (!mutex->threadQueue.isEmpty()) - mutex->threadQueue.wakeupSingleThreadWaitQueue(true); + mutex->threadQueue.wakeupSingleThreadWaitQueue(true, true); } // currentThread->cancelState = currentThread->cancelState & ~0x10000; } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index fbf498dbb..b53d04ed8 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -758,14 +758,14 @@ namespace coreinit } // returns true if thread runs on same core and has higher priority - bool __OSCoreShouldSwitchToThread(OSThread_t* currentThread, OSThread_t* newThread) + bool __OSCoreShouldSwitchToThread(OSThread_t* currentThread, OSThread_t* newThread, bool sharedPriorityAndAffinityWorkaround) { uint32 coreIndex = OSGetCoreId(); if (!newThread->context.hasCoreAffinitySet(coreIndex)) return false; // special case: if current and new thread are running only on the same core then reschedule even if priority is equal // this resolves a deadlock in Just Dance 2019 where one thread would always reacquire the same mutex within it's timeslice, blocking another thread on the same core from acquiring it - if ((1<context.affinity && currentThread->context.affinity == newThread->context.affinity && currentThread->effectivePriority == newThread->effectivePriority) + if (sharedPriorityAndAffinityWorkaround && (1<context.affinity && currentThread->context.affinity == newThread->context.affinity && currentThread->effectivePriority == newThread->effectivePriority) return true; // otherwise reschedule if new thread has higher priority return newThread->effectivePriority < currentThread->effectivePriority; @@ -791,7 +791,7 @@ namespace coreinit // todo - only set this once? thread->wakeUpTime = PPCInterpreter_getMainCoreCycleCounter(); // reschedule if thread has higher priority - if (PPCInterpreter_getCurrentInstance() && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread)) + if (PPCInterpreter_getCurrentInstance() && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread, false)) PPCCore_switchToSchedulerWithLock(); } return previousSuspendCount; @@ -948,7 +948,7 @@ namespace coreinit OSThread_t* currentThread = OSGetCurrentThread(); if (currentThread && currentThread != thread) { - if (__OSCoreShouldSwitchToThread(currentThread, thread)) + if (__OSCoreShouldSwitchToThread(currentThread, thread, false)) PPCCore_switchToSchedulerWithLock(); } __OSUnlockScheduler(); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.h b/src/Cafe/OS/libs/coreinit/coreinit_Thread.h index fdbcfea7c..8b144bd3d 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.h @@ -126,8 +126,8 @@ namespace coreinit // counterparts for queueAndWait void cancelWait(OSThread_t* thread); - void wakeupEntireWaitQueue(bool reschedule); - void wakeupSingleThreadWaitQueue(bool reschedule); + void wakeupEntireWaitQueue(bool reschedule, bool sharedPriorityAndAffinityWorkaround = false); + void wakeupSingleThreadWaitQueue(bool reschedule, bool sharedPriorityAndAffinityWorkaround = false); private: OSThread_t* takeFirstFromQueue(size_t linkOffset) @@ -611,7 +611,7 @@ namespace coreinit // internal void __OSAddReadyThreadToRunQueue(OSThread_t* thread); - bool __OSCoreShouldSwitchToThread(OSThread_t* currentThread, OSThread_t* newThread); + bool __OSCoreShouldSwitchToThread(OSThread_t* currentThread, OSThread_t* newThread, bool sharedPriorityAndAffinityWorkaround); void __OSQueueThreadDeallocation(OSThread_t* thread); bool __OSIsThreadActive(OSThread_t* thread); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp b/src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp index 68cb22b39..b33aa888b 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp @@ -128,7 +128,8 @@ namespace coreinit // counterpart for queueAndWait // if reschedule is true then scheduler will switch to woken up thread (if it is runnable on the same core) - void OSThreadQueueInternal::wakeupEntireWaitQueue(bool reschedule) + // sharedPriorityAndAffinityWorkaround is currently a hack/placeholder for some special cases. A proper fix likely involves handling all the nuances of thread effective priority + void OSThreadQueueInternal::wakeupEntireWaitQueue(bool reschedule, bool sharedPriorityAndAffinityWorkaround) { cemu_assert_debug(__OSHasSchedulerLock()); bool shouldReschedule = false; @@ -139,7 +140,7 @@ namespace coreinit thread->state = OSThread_t::THREAD_STATE::STATE_READY; thread->currentWaitQueue = nullptr; coreinit::__OSAddReadyThreadToRunQueue(thread); - if (reschedule && thread->suspendCounter == 0 && PPCInterpreter_getCurrentInstance() && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread)) + if (reschedule && thread->suspendCounter == 0 && PPCInterpreter_getCurrentInstance() && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread, sharedPriorityAndAffinityWorkaround)) shouldReschedule = true; } if (shouldReschedule) @@ -148,7 +149,7 @@ namespace coreinit // counterpart for queueAndWait // if reschedule is true then scheduler will switch to woken up thread (if it is runnable on the same core) - void OSThreadQueueInternal::wakeupSingleThreadWaitQueue(bool reschedule) + void OSThreadQueueInternal::wakeupSingleThreadWaitQueue(bool reschedule, bool sharedPriorityAndAffinityWorkaround) { cemu_assert_debug(__OSHasSchedulerLock()); OSThread_t* thread = takeFirstFromQueue(offsetof(OSThread_t, waitQueueLink)); @@ -159,7 +160,7 @@ namespace coreinit thread->state = OSThread_t::THREAD_STATE::STATE_READY; thread->currentWaitQueue = nullptr; coreinit::__OSAddReadyThreadToRunQueue(thread); - if (reschedule && thread->suspendCounter == 0 && PPCInterpreter_getCurrentInstance() && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread)) + if (reschedule && thread->suspendCounter == 0 && PPCInterpreter_getCurrentInstance() && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread, sharedPriorityAndAffinityWorkaround)) shouldReschedule = true; } if (shouldReschedule)