Skip to content

PR #137 fiber-scheduler verification: ps2xTest build fixes + BUG-024 (open)#1

Open
TH3BACKLOG wants to merge 25 commits into
mainfrom
work/laptop-session-0519
Open

PR #137 fiber-scheduler verification: ps2xTest build fixes + BUG-024 (open)#1
TH3BACKLOG wants to merge 25 commits into
mainfrom
work/laptop-session-0519

Conversation

@TH3BACKLOG

Copy link
Copy Markdown
Owner

Summary

  • Fixes ps2xTest's build so it links/runs again: 6 stale setPadOverrideState/clearPadOverrideState call sites in pad_input_tests.cpp (pre-existing port-arg drift), and ps2xTest/CMakeLists.txt missing links to ps2_runner_stubs_a..h (mirrors ps2EntryRunner's link graph).
  • Running the now-building ps2x_tests.exe did not reach the 367/367 baseline from the isolated PR #137 worktree test: 2 VIF1/DMAC-dispatch-on-store failures, 2 TerminateThread/WaitSema-teardown failures, and a hard crash (STATUS_STACK_OVERFLOW, exit code -1073741571) reproduced at an identical point across runs -- logged as BUG-024 in BUG_LOG.md.
  • Documentation updated (CLAUDE.md, PS2_PROJECT_STATE.md, BUG_LOG.md, handoff) to reflect that PR #137's test-directory deletion (previously planned) is blocked until BUG-024 is root-caused.

Note: this branch also carries the prior in-progress PR #137 integration work (commits 6a23062/9656ab8 etc.) already pushed to main on this fork in earlier sessions -- main here has been significantly behind for a while, so this PR's diff spans the full session history, not just this fix.

Test plan

  • Root-cause BUG-024's stack-overflow crash
  • Root-cause the 3 VIF1/TerminateThread logic failures
  • Re-run ps2x_tests.exe, confirm full pass count
  • Boot-verify ps2EntryRunner.exe against SLUS_214.42

Sinan-Karakaya and others added 12 commits June 6, 2026 00:15
…ecomp process (#128)

* feat(recomp): Reduce recompiler output memory usage

Stream output generation, add low-memory config controls, and avoid
pathological indirect-jump switch expansion in generated C++.

Low-memory mode now avoids retaining per-instruction disassembly strings
while still emitting asm comments during output generation. Output workers
are bounded/configurable, combined output is streamed, and decoded buffers
are released after generation.

Also document the new output memory settings.

* fix(recomp): added tests for unregistered JR/JALR, updated fallback logic to cover JR/JALR, moved Rabbitizer formatting into R5900Decoder
* feat: modularize elf analyzer
feat: added experimental sce symbol scanner
feat: change analyzer order
feat: small optimizations on analyzer

* feat: remove example_config.toml because its causing confusion on some people

* feat: embed sce symbol but leave optional import path
feat: killed skip function on analyzer but leave it so you can skip manual if you want

* feat: pin elfio tag

* feat: manually create string view with size

* feat: update ghidra script
* feat: invert codegen hight to low convertion
feat: added copy and GetEntryAddress
feat: handle truncated DMAC

* feat: always use address on analyzer now

* feat: correct pick syscalls ID

* feat: added deci2Call

* feat: added wip dbcmain IOP

* feat: added InitTLB
feat: added err logs on thread for debug sus crash

* fix: fix SetupHeap for strange cases

* feat: fix incorrect SetupHeap test(it use a wrong idea on how heap allocate memory)

* feat: added memalign and memalign_r
feat: added GetOsdConfigParam2 and  SetOsdConfigParam2 but idk if was a good idea

* feat: added more memory stuff

* feat: back to library functions
* [runtime] Memory swizzling rework

Reimplementation of memory swizzling supporting all valid GS texture
storage types.

An additional optimization was done to improve the speed of lookups.
Since page access is linear, we can precompute the non-linear bit
(blocks and columns) for an entire page for each format. This reduces
the runtime calculation to just calculating the correct page in 2d
memory space and then performing a lookup in the table to get the
location in memory inside a page. Since all page accesses are the same,
this works across all pages. Speed improvements should be compounded by
the number of accesses but there might be some reductions as a result of
actually implementing formats that were not implemented before.

I took care to do this with simple math. GS swizzling is complicated and
optimizing it for debug builds is outside the scope of my current work.

This is just the standalone code. It will be worked into the current
rasterizer in a follow up commit.

* [runtime] Reimplement GS host -> local transfers

Reimplement the host -> local transfers using new swizzling code

* [runtime] Reimplement sampling with new swizzling

Reimplements the rasterizer sampling code to use the new swizzling code

* [runtime] Replace fb writes with new swizzling

Replace the framebuffer write code with the new swizzling code

* [runtime] Reduce vram read/write to func lookup

Reduces the vram read write switches to a function lookup

* [tests] fix GS tests not handling depth test

* [runtime] z interpolation and write support

Add support for interpolating z from primitives and writing to the z
buffer after z testing and masking

* [runtime] local to local transfer rework

* [runtime] host readout uses new sizzling code

plus cleanup some dead code

* [runtime] rework local->host transfer

update to the new swizzling code

* [runtime] update clear function with new swizzling
* fix: semaphore syscalls return sid on success instead of KE_OK

The PS2 EE BIOS returns the semaphore ID on success for all semaphore
syscalls, not zero. PollSema, WaitSema, SignalSema, and DeleteSema were
all returning KE_OK (0) on success, which breaks games that compare the
return value against the semaphore ID.

DQ8's init code polls a mutex semaphore at 0x12A670 and checks the result
against the semaphore ID using bne. With KE_OK returned, the comparison
always fails and the game spins forever at PC 0x164978 and never starts.
Other games that follow the same EE BIOS convention would hit the same
problem. CreateSema already returned the ID correctly; this brings the
other four operations in line with the same convention.

The fix is five changes in Sync.cpp: the four return sites are updated to
return sid, and the count-decrement guard in WaitSema is changed from
ret == 0 to ret >= 0. The guard change is necessary because ret is now
seeded to sid (a positive value) on the success path, and the old equality
check would have silently stopped decrementing the semaphore count. Error
paths (KE_WAIT_DELETE, KE_RELEASE_WAIT) are negative and still correctly
bypass the decrement.

Tests are updated to expect sid instead of KE_OK on success paths, and new
test cases cover the blocked-wait-then-signal path (the actual DQ8 scenario),
force-release via ReleaseWaitThread, and the count-decrement guard directly.

* fix: update stale KE_OK assertions in expansion and SIF RPC tests

Two test files were not updated alongside the semaphore return-value change.
PollSema and SignalSema now return sid on success; update the four assertions
that expected KE_OK on those success paths.

* fix: tighten WaitSema count-decrement guard to ret == sid

ret is seeded to sid on success and only ever overwritten with negative
error codes, so ret == sid precisely expresses "this wait acquired this
semaphore" — more explicit than the looser ret >= 0.

Suggested by ran-j in PR review.
- missing RGBA5551 to RGBA8888 conversion in lookupClut
- round depth up for triangles (Code Veronica text bug)
* feat: added ffmepg as dependency

* feat: wip decoder video

* feat: some perf and cleanup

* feat:  added generic MPEG stream notification

* feat: CMakeLists.txt in ps2xStudio to configure SDL2 build options for static linking.
fix: fix ffmpeg setup for linux
fix: now MPEG decoder now identify that movie has ended and can play again anytime
feat: better audio stub to not block games

* feat: fix expansion test

* feat: foo

* a

* feat: finally added a helper to to prevent thread starvation

* feat: added basic  vu0 code execution

* feat: added yield Guest Execution After Wake to prevent deadlock

* feat: added  options  on  cmake for logs
feat: better input for keyboard pad

* feat: small corrections like top and itop vu branches etc

* feat: changes

* feat: working feature

* feat: fatal frame iop

* feat: test fix
feat: z buffer fix

* fix: gix GsPutIMR IMR

* feat: added rl imgui

* feat: added helper to get snapshot

* feat: added debug panel consuming snapshots

* feat: added pad snapshot
feat: added RCP debug events

* feat: final cleanup from old code

* feat: added EE timer counter
feat: applyed sound driver for Lotr
feat: better check for sound driver compat layout
feat: enquee and cosumed DMa cause
feat: added Pad execCMd
feat: update GS vsync signal flag
feat: custom IOPs for LotR

* feat: small cleanups

* fix: fix wrong import
…tructions (#141)

* feat: add sses2 support for PADDSW and PSUBSW instructions

* Fixed SSE4.1 PADDSW/PSUBSW implementations
* feat: added guestBranchKind enum to categorize branch types
feat: added missingFunctionPolicy enum to define behaviors for missing function scenarios
refactor: added handle guest branches and report missing functions
feat lookupFunction to utilize new dispatch logic and improve error handling for unregistered functions

* fix: fix test conflict

* feat: added debug sound driver logs

* feat: emmiter for return

* feat: added recompiler reporter
feat: added strict diagnostics flag for heavy debug calls

* feat: staticc table insted of hashmap for runtime

* feat: back file to ignore

* feat: explode code across helpers and classes

* feat: update codegen test
feat: better guest nop check

* feat: fix link problem on linux

* feat: fix Segmentation fault
…(#145)

updateGsCsrFieldForVSync runs on the detached vsync worker and updated the
FIELD bit with a non-atomic read-modify-write of GSRegisters::csr while
guest threads concurrently read/write the same word (MMIO read32/read64 and
the W1C handling in write32/write64) and the GIF path sets SIGNAL/FINISH.
Even though the writers touch disjoint bits, a whole-word RMW loses the
other side's update: a clobbered SIGNAL/FINISH set hangs a game
synchronizing on GS completion, a clobbered W1C clear re-asserts a handled
interrupt, and a clobbered FIELD toggle stalls interlace field polling.
ThreadSanitizer flags the race on main (updateGsCsrFieldForVSync vs
PS2Memory::write64 and GS::writeRegister).

Make the field std::atomic<uint64_t> and perform every update as a single
atomic RMW:

- vsync FIELD toggle -> fetch_or / fetch_and
- MMIO W1C writes -> compare_exchange loop in shared helpers (a
  load-then-store pair would still race); a 32-bit store to the CSR's upper
  dword previously bypassed the W1C special case entirely and went through
  the plain merge branch - both halves now share the same atomic helper
  with unchanged guest-visible semantics
- GIF SIGNAL/FINISH -> fetch_or
- reads -> load()

std::atomic<uint64_t> is lock-free on all supported targets (static_assert
added), the struct's size/alignment asserts are unchanged, GSRegisters is
never copied by value, and default (seq_cst) ordering is used throughout -
these operations are rare (vblank ticks, GIF signals, CSR MMIO), so
reviewability wins over micro-optimization.

New regression test: two racer threads each own one status bit (SIGNAL /
FINISH) and loop 80k GIF-set + W1C-clear cycles verifying their own bit
after each half-op while the vsync worker toggles FIELD. Fails 20/20 runs
against the previous code, passes 50/50 with the fix, ~350ms runtime, no
sanitizer needed.

@TH3BACKLOG TH3BACKLOG left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go

ran-j and others added 13 commits July 7, 2026 10:14
* feat: added guestBranchKind enum to categorize branch types
feat: added missingFunctionPolicy enum to define behaviors for missing function scenarios
refactor: added handle guest branches and report missing functions
feat lookupFunction to utilize new dispatch logic and improve error handling for unregistered functions

* fix: fix test conflict

* feat: added debug sound driver logs

* feat: emmiter for return

* feat: added recompiler reporter
feat: added strict diagnostics flag for heavy debug calls

* feat: staticc table insted of hashmap for runtime

* feat: back file to ignore

* feat: explode code across helpers and classes

* feat: update codegen test
feat: better guest nop check

* feat: fix link problem on linux

* feat: fix Segmentation fault

* feat: added recompile replace for DMA and MMIO
feat: added a clean memory helpers
feat: use memory helpers across the project
feat: fix ucrt on msvc

* feat: undo messup merge
Replace the per-guest-thread std::thread model (g_hostThreads) with an N=1
cooperative fiber scheduler: exactly one host executor thread runs all guest
EE threads as fibers, highest priority first, FIFO within a priority, with
cooperative yield points sampled every 128 back-edges. This matches the real
EE's single-core execution model that games' kernel-object usage assumes
(semaphores as ownership tokens, priority scheduling, RotateThreadReadyQueue
rotation) and removes the cross-thread races and lock-order deadlocks of the
previous model.

Blocking syscalls park through a publish -> arm_park -> block_current
protocol whose wake_pending handshake cannot lose wakeups; all wait sites
re-check their predicates in Mesa-style loops. Host workers (IRQ, vsync,
alarm, RPC) either wake fibers through gated entry points (with
{generation,tid} tokens rejecting stale wakeups after tid reuse) or borrow
the guest token via AsyncGuestScope. Host threads carrying a guest tid get
full ThreadInfo wait bookkeeping (wait lists, ReleaseWaitThread targeting)
but never park on the fiber scheduler; they poll with bounded backoff.

Four fiber backends: ucontext (POSIX default), Win32 Fibers (_WIN32),
SceFiber (PLATFORM_VITA; compiles against a real VitaSDK, untested on
hardware, no guard page - documented), and pthread (PS2X_FIBER_PTHREAD=ON,
exists so ThreadSanitizer can instrument the scheduler). PS2X_SANITIZE wires
TSan/ASan through every target from the top-level CMakeLists.

Preserves upstream's guest-visible semantics (#136 sid-on-success semaphore
returns throughout, including older tests).

Bugs found and fixed while hardening: lost ReleaseWaitThread during Mesa
retries; alarm-worker use-after-free vs CancelAlarm; non-fiber WaitEventFlag
and WaitForNextVSyncTick returning without waiting; INTC/DMAC enable-mask
races; a terminate or resume wake dropped in the publish->park window; a
tid-recycling ABA in join_fiber; main-thread guest identity (tid 1)
restored to upstream semantics. Dogfooding against a real commercial title found
more: executor/worker token-handoff starvation, fixed across three layers
(the executor's defer-to-parked-worker predicate, yield_point's fiber-side
handoff, and a dispatchLoop back-edge hook so cross-function spins reach
both); sceSifRpcLoop's stub returning instead of parking, which
monopolized the executor; async callback stacks carved from top-of-RAM
inside the guest's own main stack, relocated to kernel-reserved memory
(0x80000-0x100000); requestStop joining workers from guest context
(deadlock - now signal-only); DMAC dispatch borrowing the guest token from
guest context (fatal abort - now dispatches inline); per-OS-thread
thread_local state shared across fibers under N=1 - the dispatch-history
ring, the syscall-override reentrancy guard, and the RPC-invoke/inline-
DMAC/MPEG-callback scratch stacks - all now fiber-owned or per-invocation;
pthread-backend fiber threads not marked as guest context, self-deadlocking
the is_guest_thread gates; and join_fiber flooring the joiner one priority
level below the target (t->priority + 1) instead of at the target's own
level, so an equal-priority sibling of the target kept the joiner off the
run-queue head and TerminateThread never returned (DQ8: main thread
terminating a same-priority worker while a sibling stayed runnable). EE
priority is "lower number = higher"; flooring at the target's own level lets
the joiner tie the siblings and cycle back via FIFO rotation to observe the
target finish and return.

Tests: passing across ucontext and pthread backends (deterministic across
repeated runs), TSan-clean (zero warnings) with the three documented
intentional hardware-modeled vsync/GS-CSR reports suppressed via
ps2xTest/tsan.supp, ASan/LSan clean. Scheduler suites cover the
wakeup-window protocol, suspend/resume gating, tid reuse, terminate windows,
priority rotation and join floors (including a SchedulerJoinStarvation
regression: a higher-priority joiner terminating a target with an
equal-priority runnable sibling, asserting TerminateThread returns within a
bounded number of rounds), shutdown ordering, and borrowed workers, with
regression tests that fail against the unfixed code. The workload dogfood
regression suite (ps2_scheduler_workload_regression_tests.cpp) adds targeted
coverage for the bugs above: token handoff, RPC-loop park, recovery
isolation, guest-context stop, kernel stack pool, DMAC guest dispatch,
stack isolation, and override isolation.
…en-drop, fix log-once backoff warning

- Extract withGuestTokenDropped() as the single place that knows the
  async_guest_end/begin bracketing for a non-fiber wait pause; both
  NonFiberBackoff::step's sleep and nonFiberBlockBackoff's yield now
  route through it instead of duplicating the drop/reacquire branch.
- Fix a real bug in NonFiberBackoff::step: the cap-reached warning was
  meant to fire once, but spins froze at kMaxSpins so `spins ==
  kMaxSpins` stayed true and it re-fired every iteration after the cap.
  Now increments-and-compares so it fires exactly once. Also drop the
  now-redundant spins < kMaxSpins guard around the delay ramp since
  delay already self-clamps at the 1ms cap.
- Fold arm_park + block_current + the conditional step into
  NonFiberBackoff::wait(onFiber), and make step() private so no caller
  can feed it a fiber block result. Route WaitSema, WaitEventFlag,
  SleepThread, and WaitForNextVSyncTick's two block_current sites
  through it, preserving each site's exact lock handling and the
  arm-before-block ordering.
After hard-resetting to pr-137-latest, the new tree's root sits one
level higher than the old layout, surfacing ~72 untracked paths that
are local-only tools, dumps, and asset directories rather than repo
content.
classified.csv/gaps.csv/suspects.csv/PNG are 2026-06-22 gap-scan
tool output; the PSD is an unrelated UI mockup. Neither belongs in
the tracked source tree.
@TH3BACKLOG
TH3BACKLOG force-pushed the work/laptop-session-0519 branch from 5586c13 to 73d4e49 Compare July 12, 2026 00:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants