mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
The review of the system-event change (Ev-1) found the table still filling. Entries in host memory now count as live for good in the full-table scan, but their objects are freed: ov_movie2.c ov_dtor freed the Video with in_ev and out_ev still bound, so each decoder left two dead entries that nothing reclaimed (AUDIT F-1-08 measured about two entries per reload of a save, which E-4's verifier tied to the decoder pair), and their four kernel handles stayed open. A long session would end with "kern_bind_system_event: table full" once no dead guest entry was left to take. kernel.c gains kern_unbind_system_event(c, ev). On a guest thread it calls the SDK's nn::os::DestroySystemEvent, which zeroes the state byte and closes both handles, and then removes the entry under g_se_lock (the last entry takes its place). The destroy comes first, so a signal still in flight finds the event destroyed or its handle no longer naming it and skips it, rather than finding no entry and binding again on a host thread. Off a guest thread, or when the SDK lacks the function (logged once), only the entry goes. ov_dtor calls it for each bound event after finalize has joined the decoder and convert threads, then frees the Video. net_offline.c frees its HostTask with a bound event in the same way; that file is outside this change and is handed to the coordinator. Not reproduced before the change in a game run. tests/system_event_check.c fills the table with live guest events and the movie's two host-memory events, unbinds one: it leaves the table, its handles are closed and its state byte is 0, the other keeps its entry, and a second unbind changes nothing; with the table full again and the last host entry unbound, a new guest bind succeeds where it would have ended with "table full". The check passes in both modes, movie_planes_check passes, and the compile check of every runtime file (host.h changed) passes, adding no warnings in kernel.c or ov_movie2.c with -Wall -Wextra. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
63 lines
2.7 KiB
Python
63 lines
2.7 KiB
Python
"""Build and run tests/system_event_check.c: the system-event table of runtime/kernel.c (kern_bind_system_event,
|
|
kern_signal_system_event, kern_clear_system_event, kern_unbind_system_event) against destroyed events, reused handle
|
|
numbers, a host thread racing a destroy and bind, a full table holding events in host memory, and host-memory events
|
|
unbound before their memory is freed; with the platform layer linked and the rest of the runtime stubbed. No game files: the module layout the runtime includes is a stand-in written to the test's build folder.
|
|
|
|
Usage: system_event_check.py
|
|
"""
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
MSYS_BIN = Path(r'C:\msys64\ucrt64\bin')
|
|
|
|
|
|
def compiler():
|
|
if os.environ.get('CC'):
|
|
return os.environ['CC']
|
|
if os.name == 'nt' and (MSYS_BIN / 'clang.exe').exists():
|
|
return str(MSYS_BIN / 'clang.exe')
|
|
return shutil.which('clang') or 'clang'
|
|
|
|
LAYOUT = '''// A stand-in for the generated layout.h (tests/system_event_check.py).
|
|
#pragma once
|
|
#define MOD_MAIN_BASE 0x7000000000ull
|
|
#define MOD_MAIN_SIZE 0x4ea0000ull
|
|
'''
|
|
|
|
|
|
def main():
|
|
work = ROOT / 'build' / 'test' / 'system_event'
|
|
work.mkdir(parents=True, exist_ok=True)
|
|
(work / 'layout.h').write_text(LAYOUT)
|
|
win = os.name == 'nt'
|
|
exe = work / ('system_event_check.exe' if win else 'system_event_check')
|
|
env = dict(os.environ)
|
|
if win and MSYS_BIN.exists():
|
|
env['PATH'] = str(MSYS_BIN) + os.pathsep + env.get('PATH', '')
|
|
rt = ROOT / 'runtime'
|
|
plat = [rt / 'platform' / ('win32.c' if win else 'linux.c'), rt / 'platform' / 'common.c']
|
|
libs = ['-lbcrypt', '-lws2_32', '-lpsapi', '-lwinmm', '-ldbghelp', '-lshell32', '-lole32', '-luser32', '-ladvapi32'] if win \
|
|
else ['-lpthread']
|
|
subprocess.check_call([compiler(), '-O1', '-g', '-Wall', '-Wno-unused-function', '-fno-strict-aliasing',
|
|
'-march=x86-64-v3', '-I', str(rt), '-I', str(work),
|
|
str(ROOT / 'tests' / 'system_event_check.c'), *map(str, plat), '-o', str(exe), '-lm', *libs],
|
|
env=env)
|
|
rc = subprocess.run([str(exe)], env=env, timeout=300).returncode
|
|
# Every entry live: the next bind must end the process with "table full" (rt_fatal's stub exits 3).
|
|
p = subprocess.run([str(exe), 'full'], env=env, timeout=300, capture_output=True, text=True)
|
|
sys.stdout.write(p.stdout)
|
|
if p.returncode != 3 or 'table full' not in p.stdout:
|
|
print(f'FAIL: a bind with every entry live must end with "table full" (exit {p.returncode})')
|
|
rc = rc or 1
|
|
else:
|
|
print('system_event_check full: ok (table full with every entry live)')
|
|
return rc
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|