mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Kernel waits: Fix VPL/FPL and msgpipe waits around callbacks, report timeout left
Verified against pspautotests threads/callbacks/waittypes: - __KernelThreadingInit() cleared the wait type callback table after __KernelMemoryInit() had registered VPL and FPL in it, so a VPL or FPL wait interrupted by a callback was never paused or resumed, and could hang forever. - A msgpipe deleted during a callback left its waiter waiting, instead of waking it with WAIT_DELETE. - A wait that got its object during a callback reported no time left; put the timer back before trying to unlock, so the unlock writes what remains. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
acd2738b7a
commit
24b41d3f23
3 files changed
+21
-10
No files matched your search
@@ -150,8 +150,8 @@ template <typename WaitInfoType, typename PauseType>
|
||||
WaitBeginEndCallbackResult WaitBeginCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer, std::vector<WaitInfoType> &waitingThreads, std::map<SceUID, PauseType> &pausedWaits, bool doTimeout = true) {
|
||||
SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;
|
||||
|
||||
// This means two callbacks in a row. PSP crashes if the same callback waits inside itself (may need more testing.)
|
||||
// TODO: Handle this better?
|
||||
// Shouldn't happen: each nesting level pauses under its own key, and on hardware a callback can
|
||||
// nest only one level (a CB wait that would go deeper never returns.)
|
||||
if (pausedWaits.find(pauseKey) != pausedWaits.end()) {
|
||||
return WAIT_CB_SUCCESS;
|
||||
}
|
||||
@@ -222,6 +222,14 @@ WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackI
|
||||
|
||||
// TODO: Don't wake up if __KernelCurHasReadyCallbacks()?
|
||||
|
||||
// The timeout kept running during the callback. Put the timer back first, so that an unlock
|
||||
// reports the time that's left.
|
||||
s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks(currentMIPS);
|
||||
const bool hasTimer = timeoutPtr != 0 && waitTimer != -1 && waitDeadline != 0;
|
||||
if (hasTimer) {
|
||||
CoreTiming::ScheduleEvent(cyclesLeft < 0 ? 0 : cyclesLeft, waitTimer, threadID);
|
||||
}
|
||||
|
||||
bool wokeThreads;
|
||||
// Attempt to unlock.
|
||||
if (TryUnlock(ko, waitData, error, 0, wokeThreads)) {
|
||||
@@ -229,20 +237,18 @@ WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackI
|
||||
}
|
||||
|
||||
// We only check if it timed out if it couldn't unlock.
|
||||
s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks(currentMIPS);
|
||||
if (cyclesLeft < 0 && waitDeadline != 0) {
|
||||
if (hasTimer) {
|
||||
CoreTiming::UnscheduleEvent(waitTimer, threadID);
|
||||
}
|
||||
if (timeoutPtr != 0 && waitTimer != -1) {
|
||||
Memory::WriteOrException_U32(0, timeoutPtr);
|
||||
}
|
||||
|
||||
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
|
||||
return WAIT_CB_TIMED_OUT;
|
||||
} else {
|
||||
if (timeoutPtr != 0 && waitTimer != -1) {
|
||||
CoreTiming::ScheduleEvent(cyclesLeft, waitTimer, __KernelGetCurThread());
|
||||
}
|
||||
return WAIT_CB_RESUMED_WAIT;
|
||||
}
|
||||
return WAIT_CB_RESUMED_WAIT;
|
||||
}
|
||||
|
||||
// Meant to be called in a registered end callback function for a wait type.
|
||||
|
||||
@@ -566,7 +566,11 @@ static void __KernelMsgPipeEndCallback(SceUID threadID, SceUID prevCallbackId) {
|
||||
MsgPipe *ko = uid == 0 ? NULL : kernelObjects.Get<MsgPipe>(uid, error);
|
||||
|
||||
if (ko == NULL) {
|
||||
ERROR_LOG_REPORT(Log::sceKernel, "__KernelMsgPipeEndCallback: Invalid object");
|
||||
// Deleted during the callback.
|
||||
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
|
||||
if (timeoutPtr != 0 && waitTimer != -1)
|
||||
Memory::WriteOrException_U32(0, timeoutPtr);
|
||||
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -782,7 +782,8 @@ void __KernelThreadingInit() {
|
||||
u32 blockSize = sizeof(idleThreadCode) + ARRAY_SIZE(threadHacks) * 2 * 4; // The thread code above plus 8 bytes per "hack"
|
||||
|
||||
dispatchEnabled = true;
|
||||
memset(waitTypeFuncs, 0, sizeof(waitTypeFuncs));
|
||||
// Don't clear waitTypeFuncs here: __KernelMemoryInit() registers VPL and FPL before this runs.
|
||||
// Every entry is set again by its module's init anyway.
|
||||
|
||||
__SetCurrentThread(NULL, 0, NULL);
|
||||
g_inCbCount = 0;
|
||||
|
||||
Reference in new issue
Block a user