Feature/random fixes platform support (#179)

* feat: implement memory card IOP

* feat: IOP trace

* feat: move IOP logic to ps2xIOP
refactor: small refactor on audio api on runtime

* feat: android support
feat: prevent race on GS
feat: a bit cleanup and reimplement on memory card

* feat: finish guest thread
feat: android build support
feat: vita build support with suspicious setup scripts

* feat: remove idea from track
This commit is contained in:
Ranieri
2026-07-22 02:37:53 -03:00
committed by GitHub
parent 1176609890
commit f3687c5ae6
40 changed files with 1133 additions and 146 deletions
+135 -1
View File
@@ -238,7 +238,7 @@ namespace
constexpr uint32_t kAsyncCounterAddr = 0x2400u;
void testWaitForAsyncCounter(uint8_t *rdram, R5900Context *ctx, PS2Runtime *)
void testWaitForAsyncCounter(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
if (!rdram || !ctx)
{
@@ -251,6 +251,10 @@ namespace
std::memcpy(&counter, rdram + kAsyncCounterAddr, sizeof(counter));
if (counter == 0u)
{
if (runtime != nullptr)
{
runtime->yieldGuestExecutionAfterWake();
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
} while (counter == 0u);
@@ -437,6 +441,136 @@ void register_ps2_runtime_expansion_tests()
t.IsTrue(peerRanAfterHandoff, "wake handoff should let the peer acquire guest execution before returning");
});
tc.Run("recursive guest execution acquisition does not advance the handoff epoch", [](TestCase &t)
{
PS2Runtime runtime;
const uint64_t initial = runtime.guestExecutionHandoffEpochSnapshot();
PS2Runtime::GuestExecutionScope outer(&runtime);
const uint64_t afterOuter = runtime.guestExecutionHandoffEpochSnapshot();
t.Equals(afterOuter, initial + 1u, "outer acquisition should advance the epoch exactly once");
{
PS2Runtime::GuestExecutionScope inner(&runtime);
t.Equals(runtime.guestExecutionHandoffEpochSnapshot(), afterOuter,
"recursive acquisition must not advance the epoch");
PS2Runtime::GuestExecutionScope innermost(&runtime);
t.Equals(runtime.guestExecutionHandoffEpochSnapshot(), afterOuter,
"deeper recursive acquisitions must not advance the epoch either");
}
t.Equals(runtime.guestExecutionHandoffEpochSnapshot(), afterOuter,
"releasing recursive acquisitions must not advance the epoch");
});
tc.Run("reacquiring a depth-4 release advances the handoff epoch exactly once", [](TestCase &t)
{
PS2Runtime runtime;
PS2Runtime::GuestExecutionScope s1(&runtime);
PS2Runtime::GuestExecutionScope s2(&runtime);
PS2Runtime::GuestExecutionScope s3(&runtime);
PS2Runtime::GuestExecutionScope s4(&runtime);
const uint64_t before = runtime.guestExecutionHandoffEpochSnapshot();
{
PS2Runtime::GuestExecutionReleaseScope release(&runtime);
t.Equals(runtime.guestExecutionHandoffEpochSnapshot(), before,
"releasing guest execution must not advance the epoch");
}
t.Equals(runtime.guestExecutionHandoffEpochSnapshot(), before + 1u,
"reacquiring a depth-4 release should advance the epoch exactly once");
});
tc.Run("handoff completed before the wait does not count as a timeout", [](TestCase &t)
{
PS2Runtime runtime;
const uint64_t timeoutsBefore = runtime.guestExecutionHandoffTimeouts();
std::atomic<bool> holderAcquired{false};
std::atomic<bool> releaseHolder{false};
uint64_t baseline = 0u;
std::thread holder;
{
PS2Runtime::GuestExecutionScope mainScope(&runtime);
holder = std::thread([&]()
{
PS2Runtime::GuestExecutionScope scope(&runtime);
holderAcquired.store(true, std::memory_order_release);
while (!releaseHolder.load(std::memory_order_acquire))
{
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
});
const bool holderContending = waitUntil([&]()
{
return runtime.guestExecutionWaiterCountForTesting() > 0u;
}, std::chrono::milliseconds(250));
t.IsTrue(holderContending, "holder thread should be queued before the release");
// Token captured BEFORE releasing guest execution, like the dispatchers do
baseline = runtime.guestExecutionHandoffEpochSnapshot();
}
const bool acquired = waitUntil([&]()
{
return holderAcquired.load(std::memory_order_acquire);
}, std::chrono::milliseconds(250));
t.IsTrue(acquired, "holder should acquire guest execution after the release");
// Second waiter keeps waiters > 0 so the wait below cannot take the
// no-waiters fast path: it must recognize the epoch advance instead.
std::thread secondWaiter([&]()
{
PS2Runtime::GuestExecutionScope scope(&runtime);
});
const bool secondContending = waitUntil([&]()
{
return runtime.guestExecutionWaiterCountForTesting() > 0u;
}, std::chrono::milliseconds(250));
t.IsTrue(secondContending, "second waiter should be queued while the holder owns guest execution");
runtime.waitForGuestExecutionHandoff(baseline);
t.Equals(runtime.guestExecutionHandoffTimeouts(), timeoutsBefore,
"a handoff that completed before the wait must not count as a timeout");
releaseHolder.store(true, std::memory_order_release);
if (holder.joinable())
{
holder.join();
}
if (secondWaiter.joinable())
{
secondWaiter.join();
}
});
tc.Run("nested DeferredGuestYieldScope delivers pending only to the outermost scope", [](TestCase &t)
{
PS2Runtime runtime;
bool outerPending = false;
bool innerPending = false;
{
PS2Runtime::DeferredGuestYieldScope outer(outerPending);
{
PS2Runtime::DeferredGuestYieldScope inner(innerPending);
runtime.yieldGuestExecutionAfterWake(); // must defer instead of yielding
}
t.IsFalse(innerPending, "inner scope must not consume the deferred yield");
t.IsFalse(outerPending, "pending must only be delivered when the outermost scope closes");
}
t.IsTrue(outerPending, "outermost scope should deliver the deferred yield");
t.IsFalse(innerPending, "inner scope must stay untouched");
});
tc.Run("guest preemption policy requests a dispatcher handoff when another guest thread contends", [](TestCase &t)
{
PS2Runtime runtime;