Files
psxrecomp/runtime/tests/test_syscall_change_thread_self.py
T
Alexandros Mandravillis 8f8325a2fe traps: clear pc after a same-thread changethread
With the HLE scheduler (the default), ChangeThread to the thread that is
already running returns 1 from psx_request_thread_switch. psx_syscall case 3
then returned 0 with cpu->pc still holding the SYSCALL's own address.
Compiled callers fall through on 0, but the dirty-RAM interpreter transfers
whenever cpu->pc is non-zero, so it re-executed the SYSCALL forever.

A game that yields from its main thread this way hung at the kernel's
SYSCALL (EPC 0x650) as soon as that kernel code ran interpreted, for
example when its body did not match the compiled kernel.

Clear cpu->pc on that continuation, as the SYS01/02 continuations do. The
guest-exception syscall path returns before case 3 and is unchanged.

syscall_change_thread_self links the production traps.c into a fixture
with a one-thread TCB table at -O0 and -O2. fixture_link.py stubs every
other runtime seam with abort(), so an unexpected call fails the test.
2026-09-30 22:14:24 +03:00

22 lines
814 B
Python

"""ChangeThread to the running thread must continue after the SYSCALL.
Links the production traps.c into test_syscall_change_thread_self.c at -O0 and
-O2; every other runtime seam is an abort() stub (fixture_link.py).
"""
import argparse
import tempfile
from pathlib import Path
from fixture_link import build_and_run
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--cc', default='gcc')
args = parser.parse_args()
here = Path(__file__).resolve().parent
with tempfile.TemporaryDirectory() as root:
for opt in ('-O0', '-O2'):
build_and_run(args.cc, here, here.parent, opt, Path(root),
['traps.c'], 'test_syscall_change_thread_self.c')
print('PASS: same-thread ChangeThread continues after SYSCALL (O0/O2)')