feat(ajm): implement sceAjmBatchCancel

sceAjmBatchStart processes every job before it returns the batch id, so a
batch that has not been waited on is never pending: cancelling it returns 0.
A batch that was never issued or was already waited on returns INVALID_BATCH,
and a context id that was never issued returns INVALID_CONTEXT, as in shadPS4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
raghav314 2026-10-05 19:52:21 +05:30
parent bc24783e5b
commit c0c34cc83c
3 changed files with 28 additions and 7 deletions

View File

@ -14,13 +14,6 @@ static void AjmStub(const char* name) {
extern "C" {
int APS5_VABI sceAjmBatchCancel(uint32_t context, uint32_t batch) {
(void)context;
(void)batch;
AjmStub(__func__);
return 0;
}
int APS5_VABI sceAjmBatchJobControl(AjmBatchInfo* info, uint32_t instance, uint64_t flags, const void* sideband_input, size_t sideband_input_size, void* sideband_output, size_t sideband_output_size) {
(void)info;
(void)instance;

View File

@ -26,6 +26,7 @@ extern "C" {
// memory follow the SDK layouts.
namespace {
constexpr int SCE_AJM_ERROR_INVALID_CONTEXT = static_cast<int>(0x80930002);
constexpr int SCE_AJM_ERROR_INVALID_INSTANCE = static_cast<int>(0x80930003);
constexpr int SCE_AJM_ERROR_INVALID_BATCH = static_cast<int>(0x80930004);
constexpr int SCE_AJM_ERROR_INVALID_PARAMETER = static_cast<int>(0x80930005);
@ -1009,6 +1010,12 @@ int APS5_VABI sceAjmBatchWait(uint32_t context, uint32_t batch, uint32_t timeout
return 0;
}
int APS5_VABI sceAjmBatchCancel(uint32_t context, uint32_t batch) {
if (context == 0 || context >= g_nextContext.load(std::memory_order_relaxed)) return SCE_AJM_ERROR_INVALID_CONTEXT;
std::lock_guard lock(g_batchLock);
return g_batches.contains(batch) ? 0 : SCE_AJM_ERROR_INVALID_BATCH;
}
int APS5_VABI sceAjmBatchErrorDump(const AjmBatchInfo* info, AjmBatchError* error) {
(void)info;
if (error) std::memset(error, 0, sizeof(*error));

View File

@ -25,6 +25,7 @@ int APS5_VABI sceAjmBatchJobGetGaplessDecode(AjmBatchInfo*, std::uint32_t, void*
int APS5_VABI sceAjmBatchJobGetCodecInfo(AjmBatchInfo*, std::uint32_t, void*, std::size_t);
int APS5_VABI sceAjmBatchStart(std::uint32_t, const AjmBatchInfo*, int, AjmBatchError*, std::uint32_t*);
int APS5_VABI sceAjmBatchWait(std::uint32_t, std::uint32_t, std::uint32_t, AjmBatchError*);
int APS5_VABI sceAjmBatchCancel(std::uint32_t, std::uint32_t);
}
static void Require(bool value) { if (!value) std::abort(); }
@ -339,6 +340,25 @@ void TestBatchWaitRelease(std::uint32_t context) {
Require(sceAjmBatchWait(context, second + 1, 0, &error) == invalidBatch);
}
void TestBatchCancel(std::uint32_t context) {
constexpr int invalidContext = static_cast<int>(0x80930002);
constexpr int invalidBatch = static_cast<int>(0x80930004);
std::vector<std::uint8_t> batch(64);
AjmBatchInfo info{};
AjmBatchError error{};
std::uint32_t id = 0;
Require(sceAjmBatchInitialize(batch.data(), batch.size(), &info) == 0);
Require(sceAjmBatchStart(context, &info, 0, &error, &id) == 0);
Require(sceAjmBatchCancel(context, id) == 0);
Require(sceAjmBatchCancel(context, id) == 0);
Require(sceAjmBatchCancel(0, id) == invalidContext);
Require(sceAjmBatchCancel(context + 1, id) == invalidContext);
Require(sceAjmBatchWait(context, id, 0, &error) == 0);
Require(sceAjmBatchCancel(context, id) == invalidBatch);
Require(sceAjmBatchCancel(context, 0) == invalidBatch);
Require(sceAjmBatchCancel(context, id + 1) == invalidBatch);
}
}
int main() {
@ -362,5 +382,6 @@ int main() {
TestGaplessDecode(context);
TestCodecInfo(context);
TestBatchWaitRelease(context);
TestBatchCancel(context);
Require(sceAjmFinalize(context) == 0);
}