Kernel: Keep alarm and vtimer queues in step with their pending interrupts

Each due alarm or vtimer queues its id next to a pending interrupt, which
pops it when it runs. An alarm cancelled with its interrupt pending was
never popped, so every later alarm interrupt found the cancelled one at
the front and was dropped: no alarm ran again. Deleting a vtimer erased
its id instead, leaving the last pending interrupt with an empty list.

Both now pop a timer that's gone when its interrupt comes, as
threads/alarm/pending and threads/vtimers/pending show the PSP still runs
the rest, in order.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-19 21:41:48 +02:00
1 parent 3ac908345c
commit d42f34d3de
4 files changed
+19 -9

No files matched your search

+5
View File
@@ -70,12 +70,17 @@ public:
bool run(PendingInterrupt& pend) override
{
u32 error;
if (triggeredAlarm.empty()) {
return false;
}
int alarmID = triggeredAlarm.front();
PSPAlarm *alarm = kernelObjects.Get<PSPAlarm>(alarmID, error);
if (error)
{
// handleResult won't run, so the alarm is popped here, or every later alarm runs this one's entry.
WARN_LOG(Log::sceKernel, "Ignoring deleted alarm %08x", alarmID);
triggeredAlarm.pop_front();
return false;
}
+11 -8
View File
@@ -139,12 +139,18 @@ public:
bool run(PendingInterrupt &pend) override {
u32 error;
if (vtimers.empty()) {
return false;
}
SceUID vtimerID = vtimers.front();
VTimer *vtimer = kernelObjects.Get<VTimer>(vtimerID, error);
if (error)
if (error) {
// Deleted while its interrupt was pending. handleResult won't run, so it's popped here.
WARN_LOG(Log::sceKernel, "Ignoring deleted vtimer %08x", vtimerID);
vtimers.pop_front();
return false;
}
// Reserve some stack space for arguments.
u32 argArea = currentMIPS->r[MIPS_REG_SP];
@@ -246,12 +252,9 @@ u32 sceKernelDeleteVTimer(SceUID uid) {
return hleLogError(Log::sceKernel, error, "bad timer ID");
}
for (std::list<SceUID>::iterator it = vtimers.begin(); it != vtimers.end(); ++it) {
if (*it == vt->GetUID()) {
vtimers.erase(it);
break;
}
}
// Its entry in vtimers stays: each one belongs to a pending interrupt, which pops it when it runs
// (or right away, finding the timer gone), so erasing it here would hand that interrupt to the
// next timer's entry, or to an empty list if the handler is deleting its own timer.
return hleLogDebugOrError(Log::sceKernel, kernelObjects.Destroy<VTimer>(uid));
}
+2
View File
@@ -371,6 +371,7 @@ tests_good = [
"sysmem/volatile",
"threads/alarm/alarm",
"threads/alarm/cancel/cancel",
"threads/alarm/pending/pending",
"threads/alarm/refer/refer",
"threads/alarm/set/set",
"threads/callbacks/afterwait",
@@ -508,6 +509,7 @@ tests_good = [
"threads/vtimers/getbase",
"threads/vtimers/gettime",
"threads/vtimers/interrupt",
"threads/vtimers/pending/pending",
"threads/vtimers/refer",
"threads/vtimers/sethandler",
"threads/vtimers/settime",