From d5cec869339b31dbbe1b135632b2f01f887d64b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jul 2026 18:09:09 +0200 Subject: [PATCH] Revert "Implement exit callbacks. Attempt to solve #21828" This reverts commit e4d3cc841cb151b340e76328b3df13c50c73de06. --- Core/HLE/sceKernelThread.cpp | 82 +----------------------------------- Core/HLE/sceKernelThread.h | 9 ---- UI/EmuScreen.cpp | 41 +----------------- 3 files changed, 4 insertions(+), 128 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 52f33bd724..6dcd483909 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -300,31 +300,6 @@ public: SceUID cbId; }; -// Set when an exit callback dispatch is in flight, cleared by the action below when it returns. -static bool g_exitCallbackPending = false; - -class ActionAfterExitCallback : public PSPAction { -public: - ActionAfterExitCallback() {} - void run(MipsCall &call) override { - INFO_LOG(Log::sceKernel, "Exit callback returned (v0 = %08x). Powering down.", currentMIPS->r[MIPS_REG_V0]); - g_exitCallbackPending = false; - // The callback may have already powered down by calling sceKernelExitGame - that's fine, - // Core_Stop is idempotent. Either way, the host will see CORE_POWERDOWN and tear the game down. - Core_Stop(); - } - - static PSPAction *Create() { - return new ActionAfterExitCallback; - } - - void DoState(PointerWrap &p) override { - auto s = p.Section("ActionAfterExitCallback", 1); - if (!s) - return; - } -}; - u32 PSPThread::GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } @@ -546,7 +521,6 @@ static bool dispatchEnabled = true; static MipsCallManager mipsCalls; static int actionAfterCallback; static int actionAfterMipsCall; -static int actionAfterExitCallback; // When inside a callback, delays are "paused", and rechecked after the callback returns. static std::map pausedDelays; @@ -834,8 +808,6 @@ void __KernelThreadingInit() eventThreadEndTimeout = CoreTiming::RegisterEvent("ThreadEndTimeout", &hleThreadEndTimeout); actionAfterMipsCall = __KernelRegisterActionType(ActionAfterMipsCall::Create); actionAfterCallback = __KernelRegisterActionType(ActionAfterCallback::Create); - actionAfterExitCallback = __KernelRegisterActionType(ActionAfterExitCallback::Create); - g_exitCallbackPending = false; // Create the two idle threads, as well. With the absolute minimal possible priority. // 4096 stack size - don't know what the right value is. Hm, if callbacks are ever to run on these threads... @@ -853,7 +825,7 @@ void __KernelThreadingInit() void __KernelThreadingDoState(PointerWrap &p) { - auto s = p.Section("sceKernelThread", 1, 5); + auto s = p.Section("sceKernelThread", 1, 4); if (!s) return; @@ -889,10 +861,6 @@ void __KernelThreadingDoState(PointerWrap &p) __KernelRestoreActionType(actionAfterMipsCall, ActionAfterMipsCall::Create); Do(p, actionAfterCallback); __KernelRestoreActionType(actionAfterCallback, ActionAfterCallback::Create); - if (s >= 5) { - Do(p, actionAfterExitCallback); - __KernelRestoreActionType(actionAfterExitCallback, ActionAfterExitCallback::Create); - } Do(p, pausedDelays); @@ -1082,7 +1050,6 @@ void __KernelThreadingShutdown() { pausedDelays.clear(); threadEventHandlers.clear(); pendingDeleteThreads.clear(); - g_exitCallbackPending = false; } std::string __KernelThreadingSummary() { @@ -2722,7 +2689,7 @@ SceUID sceKernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg) if (thread) thread->callbacks.push_back(id); - return hleLogInfo(Log::sceKernel, id); + return hleLogDebug(Log::sceKernel, id); } int sceKernelDeleteCallback(SceUID cbId) @@ -3523,51 +3490,6 @@ int sceKernelRegisterExitCallback(SceUID cbId) return hleLogDebug(Log::sceKernel, 0); } -// Dispatch the exit callback registered via sceKernelRegisterExitCallback on the thread that registered -// it, so the game has a chance to run its cleanup before we shut down. Returns false if no callback can -// be dispatched (none registered, registering thread dead, etc.) - the caller should then power down -// immediately. While true is returned, __KernelIsExitCallbackPending() will report true until the -// callback returns (via the chained ActionAfterExitCallback). -bool __KernelInvokeRegisteredExitCallback() { - if (g_exitCallbackPending) { - WARN_LOG(Log::sceKernel, "__KernelInvokeRegisteredExitCallback: already in progress"); - return true; - } - - u32 error; - PSPCallback *cb = kernelObjects.Get(registeredExitCbId, error); - if (!cb) { - INFO_LOG(Log::sceKernel, "__KernelInvokeRegisteredExitCallback: no exit callback registered"); - return false; - } - - PSPThread *thread = kernelObjects.Get(cb->nc.threadId, error); - if (!thread) { - WARN_LOG(Log::sceKernel, "__KernelInvokeRegisteredExitCallback: registering thread %08x is gone", cb->nc.threadId); - return false; - } - if (thread->isStopped()) { - WARN_LOG(Log::sceKernel, "__KernelInvokeRegisteredExitCallback: registering thread %s is stopped", thread->GetName()); - return false; - } - - ActionAfterExitCallback *action = (ActionAfterExitCallback *)__KernelCreateAction(actionAfterExitCallback); - - // Exit callbacks on real PSP receive (arg1, arg2, common) where arg1/arg2 are zero for HOME-button exit. - const u32 args[] = { 0, 0, cb->nc.commonArgument }; - - INFO_LOG(Log::sceKernel, "__KernelInvokeRegisteredExitCallback: dispatching '%s' (cb %08x) on thread '%s'", - cb->nc.name, registeredExitCbId, thread->GetName()); - - g_exitCallbackPending = true; - __KernelCallAddress(thread, cb->nc.entrypoint, action, args, 3, true, registeredExitCbId); - return true; -} - -bool __KernelIsExitCallbackPending() { - return g_exitCallbackPending; -} - // Update the exit callback status? int LoadExecForUser_362A956B() { diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 41e664fbed..aff67f617d 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -451,15 +451,6 @@ void __KernelChangeThreadState(SceUID threadId, ThreadStatus newStatus); int LoadExecForUser_362A956B(); int sceKernelRegisterExitCallback(SceUID cbId); -// Dispatch the exit callback registered via sceKernelRegisterExitCallback on its registering thread, -// so the game has a chance to clean up before we shut down. Returns false if no callback can be -// dispatched (none registered, the thread is gone, etc.) - in that case, the host should proceed -// to power down immediately. -bool __KernelInvokeRegisteredExitCallback(); -// True while a previously-invoked exit callback hasn't yet returned. The host should keep running -// emulation while this is true, and only tear down after it goes false (or after a timeout). -bool __KernelIsExitCallbackPending(); - KernelObject *__KernelThreadEventHandlerObject(); SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u32 mask, u32 handlerPtr, u32 commonArg); int sceKernelReleaseThreadEventHandler(SceUID uid); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index bf49fc0be1..4b635d73c9 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -65,7 +65,6 @@ using namespace std::placeholders; #include "GPU/GPUCommon.h" #include "Core/MIPS/MIPS.h" #include "Core/HLE/sceCtrl.h" -#include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceSas.h" #include "Core/HLE/sceNet.h" #include "Core/HLE/sceDisplay.h" @@ -472,33 +471,6 @@ EmuScreen::~EmuScreen() { bootPending_ = false; } - // Catch-all for shutdown paths that didn't go through REQUEST_GAME_STOP (e.g. user navigated - // away with the back button, opened a different game, etc.). If an exit callback is registered - // and the core is still running, drive the CPU synchronously here so the callback gets a chance - // to run - there are no more host frames after this point. Capped tightly so a misbehaving - // callback can't stall shutdown. - if (!bootPending_ && PSP_IsInited() && coreState != CORE_POWERDOWN) { - const bool inflight = __KernelIsExitCallbackPending(); - if (inflight || __KernelInvokeRegisteredExitCallback()) { - INFO_LOG(Log::Loader, "EmuScreen dtor: %s exit callback synchronously before shutdown", - inflight ? "finishing in-flight" : "running"); - if (coreState == CORE_NEXTFRAME) - coreState = CORE_RUNNING_CPU; - const s64 chunkCycles = usToCycles(100000); // ~100ms of emulated time per chunk - const int maxChunks = 20; // hard cap: ~2s of emulated time total - for (int i = 0; i < maxChunks && __KernelIsExitCallbackPending(); i++) { - if (coreState != CORE_RUNNING_CPU && coreState != CORE_NEXTFRAME) - break; - PSP_RunLoopFor((int)chunkCycles); - if (coreState == CORE_NEXTFRAME) - coreState = CORE_RUNNING_CPU; - } - if (__KernelIsExitCallbackPending()) { - WARN_LOG(Log::Loader, "Exit callback didn't return in time; powering down anyway."); - } - } - } - Achievements::UnloadGame(); PSP_Shutdown(true); @@ -591,17 +563,8 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) { WARN_LOG(Log::Loader, "Can't stop during a pending boot"); return; } - // Give the game a chance to run its registered exit callback before tearing things down. - // If the dispatch took, we keep running emulation; ActionAfterExitCallback (or a direct - // sceKernelExitGame from inside the callback) flips coreState to CORE_POWERDOWN, and - // checkPowerDown() then switches us to MainScreen as usual. Pressing stop again while - // the callback is in flight, or stopping a non-running core, falls through to immediate shutdown. - if (coreState == CORE_RUNNING_CPU && !__KernelIsExitCallbackPending() && __KernelInvokeRegisteredExitCallback()) { - INFO_LOG(Log::Loader, "REQUEST_GAME_STOP: running exit callback before shutdown."); - } else { - // No callback (or already in flight, or core not running) - the destructor will take care of shutting down. - screenManager()->switchScreen(new MainScreen()); - } + // The destructor will take care of shutting down. + screenManager()->switchScreen(new MainScreen()); } else if (message == UIMessage::REQUEST_GAME_RESET) { if (bootPending_) { WARN_LOG(Log::Loader, "Can't reset during a pending boot");