Files
AnyPS5/core/libs/tests/GuestUltSync.cpp
T
Yossi Yadgar e2c38222fe feat(libSceUlt): implement the semaphore, queue, pool and ulthread option initializers
Each clears its 128-byte option structure, as sceUltMutexOptParamInitialize and sceUltUlthreadRuntimeOptParamInitialize already do.
2026-10-20 14:35:20 +03:00

96 lines
3.3 KiB
C++

#include "SceTypes.hpp"
#include <atomic>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <initializer_list>
#include <thread>
extern "C" {
int APS5_VABI sceUltInitialize();
int APS5_VABI sceUltFinalize();
int APS5_VABI _sceUltMutexCreate(void*, const char*, void*, const void*, std::uint32_t);
int APS5_VABI sceUltMutexLock(void*);
int APS5_VABI sceUltMutexTryLock(void*);
int APS5_VABI sceUltMutexUnlock(void*);
int APS5_VABI sceUltMutexDestroy(void*);
int APS5_VABI sceUltSemaphoreOptParamInitialize(void*, std::uint32_t);
int APS5_VABI sceUltQueueOptParamInitialize(void*, std::uint32_t);
int APS5_VABI sceUltQueueDataResourcePoolOptParamInitialize(void*, std::uint32_t);
int APS5_VABI sceUltWaitingQueueResourcePoolOptParamInitialize(void*, std::uint32_t);
int APS5_VABI sceUltUlthreadOptParamInitialize(void*, std::uint32_t);
}
namespace {
constexpr int Ok = 0;
constexpr int Null = -2139029503;
constexpr int State = -2139029498;
constexpr int Busy = -2139029497;
void Require(bool value, int line) {
if (value) return;
std::fprintf(stderr, "ULT sync check failed at line %d\n", line);
std::abort();
}
#define CHECK(value) Require((value), __LINE__)
struct alignas(8) Object {
std::uint8_t bytes[256];
};
void MutexTryLock() {
Object mutex{};
CHECK(sceUltMutexTryLock(nullptr) == Null);
CHECK(sceUltMutexTryLock(&mutex) == State);
CHECK(_sceUltMutexCreate(&mutex, "trylock", nullptr, nullptr, 0) == Ok);
CHECK(sceUltMutexTryLock(&mutex) == Ok);
CHECK(sceUltMutexTryLock(&mutex) == Ok);
std::atomic<int> other{0};
std::thread([&] { other = sceUltMutexTryLock(&mutex); }).join();
CHECK(other == Busy);
CHECK(sceUltMutexUnlock(&mutex) == Ok);
std::thread([&] { other = sceUltMutexTryLock(&mutex); }).join();
CHECK(other == Busy);
CHECK(sceUltMutexUnlock(&mutex) == Ok);
CHECK(sceUltMutexUnlock(&mutex) == State);
std::thread([&] {
other = sceUltMutexTryLock(&mutex);
if (other == Ok) other = sceUltMutexUnlock(&mutex);
}).join();
CHECK(other == Ok);
CHECK(sceUltMutexLock(&mutex) == Ok);
CHECK(sceUltMutexDestroy(&mutex) == Busy);
CHECK(sceUltMutexUnlock(&mutex) == Ok);
CHECK(sceUltMutexDestroy(&mutex) == Ok);
CHECK(sceUltMutexTryLock(&mutex) == State);
}
void OptParams() {
using Initializer = int (APS5_VABI *)(void*, std::uint32_t);
for (const Initializer initialize : {sceUltSemaphoreOptParamInitialize, sceUltQueueOptParamInitialize, sceUltQueueDataResourcePoolOptParamInitialize,
sceUltWaitingQueueResourcePoolOptParamInitialize, sceUltUlthreadOptParamInitialize}) {
std::uint8_t bytes[136];
std::memset(bytes, 0xa5, sizeof(bytes));
CHECK(initialize(nullptr, 0) == Null);
CHECK(initialize(bytes, 0) == Ok);
for (std::size_t index = 0; index < 128; ++index) CHECK(bytes[index] == 0);
for (std::size_t index = 128; index < sizeof(bytes); ++index) CHECK(bytes[index] == 0xa5);
}
}
}
int main(int argc, char** argv) {
if (argc != 2) return 2;
CHECK(sceUltInitialize() == Ok);
if (std::strcmp(argv[1], "mutex-trylock") == 0) MutexTryLock();
else if (std::strcmp(argv[1], "opt-params") == 0) OptParams();
else return 2;
CHECK(sceUltFinalize() == Ok);
std::puts("ULT sync tests passed");
return 0;
}