Revert "Implement exit callbacks. Attempt to solve #21828"

This reverts commit e4d3cc841c.
This commit is contained in:
Henrik Rydgård
2026-07-08 18:09:09 +02:00
parent 141b21a133
commit d5cec86933
3 changed files with 4 additions and 128 deletions
+2 -80
View File
@@ -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<SceUID, u64> 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<PSPCallback>(registeredExitCbId, error);
if (!cb) {
INFO_LOG(Log::sceKernel, "__KernelInvokeRegisteredExitCallback: no exit callback registered");
return false;
}
PSPThread *thread = kernelObjects.Get<PSPThread>(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()
{
-9
View File
@@ -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);
+2 -39
View File
@@ -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");