From 80b1c50b506bb05aa4e65ef8e93781db93d6dd92 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:16:49 +0100 Subject: [PATCH] coreinit: Fix thread scheduler not resetting quantum When a thread was force-rescheduled (e.g. via OSYieldThread) the next time it resumed it would not reset the time slice duration (remainingCycles) back to ppcThreadQuantum. As a consequence threads were often immediately rescheduled and only on the next turn they would get their full time slice. Aside from (very slightly) improving performance, this also fixes the OSDisableInterrupts warning spam in the log for TPHD. --- .../OS/libs/coreinit/coreinit_Scheduler.cpp | 5 +--- src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 30 ++++++++++++------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Scheduler.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Scheduler.cpp index d166690e..bb310778 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Scheduler.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Scheduler.cpp @@ -68,7 +68,6 @@ namespace coreinit // disables interrupts and scheduling uint32 OSDisableInterrupts() { - // todo - rename SchedulerLock.cpp/h to Scheduler.cpp and move this there? PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); if (hCPU == nullptr) return 0; @@ -77,9 +76,7 @@ namespace coreinit { // we have no efficient method to turn off scheduling completely, so instead we just increase the remaining cycles if (hCPU->remainingCycles >= 0x40000000) - { - forceLogDebug_printf("OSDisableInterrupts(): Warning - Interrupts already disabled? remCycles %08x LR %08x", hCPU->remainingCycles, hCPU->spr.LR); - } + cemuLog_log(LogType::Force, "OSDisableInterrupts(): Warning - Interrupts already disabled but the mask was still set? remCycles {:08x} LR {:08x}", hCPU->remainingCycles, hCPU->spr.LR); hCPU->remainingCycles += 0x40000000; } hCPU->coreInterruptMask = 0; diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index 2762b48f..f11225cb 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -962,6 +962,22 @@ namespace coreinit thread->wakeUpCount = thread->wakeUpCount + 1; } + uint32 s_lehmer_lcg[PPC_CORE_COUNT] = { 0 }; + + void __OSThreadStartTimeslice(OSThread_t* thread, PPCInterpreter_t* hCPU) + { + uint32 coreIndex = PPCInterpreter_getCoreIndex(hCPU); + // run one timeslice + hCPU->remainingCycles = ppcThreadQuantum; + hCPU->skippedCycles = 0; + // we add a slight randomized variance to the thread quantum to avoid getting stuck in repeated code sequences where one or multiple threads always unload inside a lock + // this was seen in Mario Party 10 during early boot where several OSLockMutex operations would align in such a way that one thread would never successfully acquire the lock + if (s_lehmer_lcg[coreIndex] == 0) + s_lehmer_lcg[coreIndex] = 12345; + hCPU->remainingCycles += (s_lehmer_lcg[coreIndex] & 0x7F); + s_lehmer_lcg[coreIndex] = (uint32)((uint64)s_lehmer_lcg[coreIndex] * 279470273ull % 0xfffffffbull); + } + OSThread_t* __OSGetNextRunableThread(uint32 coreIndex) { cemu_assert_debug(__OSHasSchedulerLock()); @@ -1088,8 +1104,9 @@ namespace coreinit cemu_assert_debug(__OSHasSchedulerLock()); cemu_assert_debug(g_isMulticoreMode == false || hostThread->selectedCore == t_assignedCoreIndex); - // load self thread + // received next time slice, load self again __OSLoadThread(hostThread->m_thread, &hostThread->ppcInstance, hostThread->selectedCore); + __OSThreadStartTimeslice(hostThread->m_thread, &hostThread->ppcInstance); } void __OSFiberThreadEntry(void* _thread) @@ -1100,21 +1117,12 @@ namespace coreinit _mm_setcsr(_mm_getcsr() | 0x8000); // flush denormals to zero #endif - uint32 lehmer_lcg = 12345; - PPCInterpreter_t* hCPU = &hostThread->ppcInstance; __OSLoadThread(hostThread->m_thread, hCPU, hostThread->selectedCore); + __OSThreadStartTimeslice(hostThread->m_thread, &hostThread->ppcInstance); __OSUnlockScheduler(); // lock is always held when switching to a fiber, so we need to unlock it here while (true) { - // run one timeslice - hCPU->remainingCycles = ppcThreadQuantum; - hCPU->skippedCycles = 0; - // we add a slight randomized variance to the thread quantum to avoid getting stuck in repeated code sequences where the duration matches the thread quantum perfectly - // this was seen in Mario Party 10 on launch where several OSLockMutex operations would align in such a way that one thread would never successfully acquire the lock - hCPU->remainingCycles += (lehmer_lcg & 0x7F); - lehmer_lcg = (uint32)((uint64)lehmer_lcg * 279470273ull % 0xfffffffbull); - if (hCPU->remainingCycles > 0) { // try to enter recompiler immediately