mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 15:00:30 +02:00
Threads: Wait timeouts work like hardware's, one rule for all of them
Every wait with a timeout behaves the same on hardware (pspautotests threads/scheduling/waittimeouts). The deadline is taken, and the alarm set up a moment later. If the deadline has passed by then, the wait fails with WAIT_TIMEOUT at once, without yielding or writing the timeout back. That's usual for 0us, half the time for 1us, and rare after; AllocateVpl does more first. Otherwise it ends max(t, 205us) + ~35us after the call. Each object had its own guess (24/245, 25/250, 20/250 and so on), and only MsgPipe had the immediate case. __KernelWaitTimesOutAtOnce() and __KernelWaitTimeoutUs() now do it for semaphores, event flags, mutexes, lwmutexes, mbx, msgpipes, fpl, vpl and WaitThreadEnd. The latency past the deadline isn't counted in the time left written back. Outcomes that hardware decides by the clock's phase (these, and sceKernelDelayThread returning at once) go with the likelier one. Ones between 50% and certain are instead spread evenly over calls, so a polling loop can't lock into never yielding (sceKernelThread section version 7). This replaces the pseudo-random choice for delays. Also adds threads/scheduling/readyqueue, which already passes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f731e45f40
commit
fb9ac8397e
10 files changed
+116
-98
No files matched your search
@@ -641,19 +641,8 @@ static void __KernelSetFplTimeout(u32 timeoutPtr)
|
||||
if (timeoutPtr == 0 || fplWaitTimer == -1)
|
||||
return;
|
||||
|
||||
int micro = (int) Memory::ReadOrException_U32(timeoutPtr);
|
||||
|
||||
// TODO: test for fpls.
|
||||
// This happens to be how the hardware seems to time things.
|
||||
if (micro <= 5)
|
||||
micro = 20;
|
||||
// Yes, this 7 is reproducible. 6 is (a lot) longer than 7.
|
||||
else if (micro == 7)
|
||||
micro = 25;
|
||||
else if (micro <= 215)
|
||||
micro = 250;
|
||||
|
||||
CoreTiming::ScheduleEvent(usToCycles(micro), fplWaitTimer, __KernelGetCurThread());
|
||||
u32 micro = Memory::ReadOrException_U32(timeoutPtr);
|
||||
CoreTiming::ScheduleEvent(usToCycles(__KernelWaitTimeoutUs(micro)), fplWaitTimer, __KernelGetCurThread());
|
||||
}
|
||||
|
||||
int sceKernelAllocateFpl(SceUID uid, u32 blockPtrAddr, u32 timeoutPtr) {
|
||||
@@ -671,6 +660,8 @@ int sceKernelAllocateFpl(SceUID uid, u32 blockPtrAddr, u32 timeoutPtr) {
|
||||
} else {
|
||||
SceUID threadID = __KernelGetCurThread();
|
||||
HLEKernel::RemoveWaitingThread(fpl->waitingThreads, threadID);
|
||||
if (__KernelWaitTimesOutAtOnce(timeoutPtr))
|
||||
return hleLogDebug(Log::sceKernel, SCE_KERNEL_ERROR_WAIT_TIMEOUT, "timed out at once");
|
||||
FplWaitingThread waiting = {threadID, blockPtrAddr};
|
||||
fpl->waitingThreads.push_back(waiting);
|
||||
|
||||
@@ -696,6 +687,8 @@ int sceKernelAllocateFplCB(SceUID uid, u32 blockPtrAddr, u32 timeoutPtr) {
|
||||
} else {
|
||||
SceUID threadID = __KernelGetCurThread();
|
||||
HLEKernel::RemoveWaitingThread(fpl->waitingThreads, threadID);
|
||||
if (__KernelWaitTimesOutAtOnce(timeoutPtr))
|
||||
return hleLogDebug(Log::sceKernel, SCE_KERNEL_ERROR_WAIT_TIMEOUT, "timed out at once");
|
||||
FplWaitingThread waiting = {threadID, blockPtrAddr};
|
||||
fpl->waitingThreads.push_back(waiting);
|
||||
|
||||
@@ -1492,18 +1485,8 @@ static void __KernelSetVplTimeout(u32 timeoutPtr)
|
||||
if (timeoutPtr == 0 || vplWaitTimer == -1)
|
||||
return;
|
||||
|
||||
int micro = (int) Memory::ReadOrException_U32(timeoutPtr);
|
||||
|
||||
// This happens to be how the hardware seems to time things.
|
||||
if (micro <= 5)
|
||||
micro = 20;
|
||||
// Yes, this 7 is reproducible. 6 is (a lot) longer than 7.
|
||||
else if (micro == 7)
|
||||
micro = 25;
|
||||
else if (micro <= 215)
|
||||
micro = 250;
|
||||
|
||||
CoreTiming::ScheduleEvent(usToCycles(micro), vplWaitTimer, __KernelGetCurThread());
|
||||
u32 micro = Memory::ReadOrException_U32(timeoutPtr);
|
||||
CoreTiming::ScheduleEvent(usToCycles(__KernelWaitTimeoutUs(micro)), vplWaitTimer, __KernelGetCurThread());
|
||||
}
|
||||
|
||||
int sceKernelAllocateVpl(SceUID uid, u32 size, u32 addrPtr, u32 timeoutPtr)
|
||||
@@ -1514,8 +1497,9 @@ int sceKernelAllocateVpl(SceUID uid, u32 size, u32 addrPtr, u32 timeoutPtr)
|
||||
VPL *vpl = kernelObjects.Get<VPL>(uid, ignore);
|
||||
if (error == SCE_KERNEL_ERROR_NO_MEMORY)
|
||||
{
|
||||
if (timeoutPtr != 0 && Memory::ReadOrException_U32(timeoutPtr) == 0)
|
||||
return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
|
||||
// Allocating does more before setting up the timeout, so short ones time out at once more often.
|
||||
if (__KernelWaitTimesOutAtOnce(timeoutPtr, 117, 17))
|
||||
return hleLogDebug(Log::sceKernel, SCE_KERNEL_ERROR_WAIT_TIMEOUT, "timed out at once");
|
||||
|
||||
if (vpl) {
|
||||
SceUID threadID = __KernelGetCurThread();
|
||||
@@ -1544,8 +1528,9 @@ int sceKernelAllocateVplCB(SceUID uid, u32 size, u32 addrPtr, u32 timeoutPtr)
|
||||
VPL *vpl = kernelObjects.Get<VPL>(uid, ignore);
|
||||
if (error == SCE_KERNEL_ERROR_NO_MEMORY)
|
||||
{
|
||||
if (timeoutPtr != 0 && Memory::ReadOrException_U32(timeoutPtr) == 0)
|
||||
return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
|
||||
// Allocating does more before setting up the timeout, so short ones time out at once more often.
|
||||
if (__KernelWaitTimesOutAtOnce(timeoutPtr, 117, 17))
|
||||
return hleLogDebug(Log::sceKernel, SCE_KERNEL_ERROR_WAIT_TIMEOUT, "timed out at once");
|
||||
|
||||
if (vpl)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user