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.
This commit is contained in:
Exzap 2023-02-22 13:16:49 +01:00
parent 9d25b88368
commit 80b1c50b50
2 changed files with 20 additions and 15 deletions

View File

@ -68,7 +68,6 @@ namespace coreinit
// disables interrupts and scheduling // disables interrupts and scheduling
uint32 OSDisableInterrupts() uint32 OSDisableInterrupts()
{ {
// todo - rename SchedulerLock.cpp/h to Scheduler.cpp and move this there?
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
if (hCPU == nullptr) if (hCPU == nullptr)
return 0; 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 // we have no efficient method to turn off scheduling completely, so instead we just increase the remaining cycles
if (hCPU->remainingCycles >= 0x40000000) if (hCPU->remainingCycles >= 0x40000000)
{ cemuLog_log(LogType::Force, "OSDisableInterrupts(): Warning - Interrupts already disabled but the mask was still set? remCycles {:08x} LR {:08x}", hCPU->remainingCycles, hCPU->spr.LR);
forceLogDebug_printf("OSDisableInterrupts(): Warning - Interrupts already disabled? remCycles %08x LR %08x", hCPU->remainingCycles, hCPU->spr.LR);
}
hCPU->remainingCycles += 0x40000000; hCPU->remainingCycles += 0x40000000;
} }
hCPU->coreInterruptMask = 0; hCPU->coreInterruptMask = 0;

View File

@ -962,6 +962,22 @@ namespace coreinit
thread->wakeUpCount = thread->wakeUpCount + 1; 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) OSThread_t* __OSGetNextRunableThread(uint32 coreIndex)
{ {
cemu_assert_debug(__OSHasSchedulerLock()); cemu_assert_debug(__OSHasSchedulerLock());
@ -1088,8 +1104,9 @@ namespace coreinit
cemu_assert_debug(__OSHasSchedulerLock()); cemu_assert_debug(__OSHasSchedulerLock());
cemu_assert_debug(g_isMulticoreMode == false || hostThread->selectedCore == t_assignedCoreIndex); 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); __OSLoadThread(hostThread->m_thread, &hostThread->ppcInstance, hostThread->selectedCore);
__OSThreadStartTimeslice(hostThread->m_thread, &hostThread->ppcInstance);
} }
void __OSFiberThreadEntry(void* _thread) void __OSFiberThreadEntry(void* _thread)
@ -1100,21 +1117,12 @@ namespace coreinit
_mm_setcsr(_mm_getcsr() | 0x8000); // flush denormals to zero _mm_setcsr(_mm_getcsr() | 0x8000); // flush denormals to zero
#endif #endif
uint32 lehmer_lcg = 12345;
PPCInterpreter_t* hCPU = &hostThread->ppcInstance; PPCInterpreter_t* hCPU = &hostThread->ppcInstance;
__OSLoadThread(hostThread->m_thread, hCPU, hostThread->selectedCore); __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 __OSUnlockScheduler(); // lock is always held when switching to a fiber, so we need to unlock it here
while (true) 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) if (hCPU->remainingCycles > 0)
{ {
// try to enter recompiler immediately // try to enter recompiler immediately