#include #include #include #include #include #include #include #include #include #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include "SceUltTypes.hpp" extern "C" int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name); extern "C" int APS5_VABI scePthreadJoin(Pthread thread, void** retval); namespace { std::mutex gMutex; std::unordered_map> gMutexes; std::unordered_map> gSemaphores; std::unordered_map gResourcePools; std::unordered_map gQueueDataPools; std::unordered_map> gQueues; std::unordered_map gRuntimes; std::unordered_map> gUlthreads; std::uint64_t alignUp(std::uint64_t value, std::uint64_t alignment) { return (value + alignment - 1u) & ~(alignment - 1u); } int semaphoreGetState(void* semaphore, std::shared_ptr* out) { std::lock_guard lock(gMutex); auto it = gSemaphores.find(semaphore); if (it == gSemaphores.end()) { return semaphore == nullptr ? ULT_ERROR_NULL : ULT_ERROR_STATE; } *out = it->second; return ULT_OK; } int queueGetState(void* queue, std::shared_ptr* out) { std::lock_guard lock(gMutex); auto it = gQueues.find(queue); if (it == gQueues.end()) { return queue == nullptr ? ULT_ERROR_NULL : ULT_ERROR_STATE; } *out = it->second; return ULT_OK; } void* APS5_VABI ulthreadRunner(void* arg) { auto* state = static_cast(arg); return reinterpret_cast(static_cast(state->_entry(state->_arg))); } } extern "C" { int APS5_VABI sceUltInitialize() { return ULT_OK; } int APS5_VABI sceUltFinalize() { std::vector> semaphores; { std::lock_guard lock(gMutex); semaphores.reserve(gSemaphores.size()); for (auto& entry : gSemaphores) { auto& state = entry.second; std::lock_guard stateLock(state->_mutex); state->_alive = false; semaphores.push_back(state); } gSemaphores.clear(); gMutexes.clear(); gResourcePools.clear(); gQueueDataPools.clear(); gQueues.clear(); gRuntimes.clear(); gUlthreads.clear(); } for (const auto& state : semaphores) { state->_available.notify_all(); } return ULT_OK; } int APS5_VABI sceUltMutexOptParamInitialize(UltMutexOptParam* optParam, std::uint32_t buildVersion) { (void)buildVersion; if (optParam == nullptr) { return ULT_ERROR_NULL; } std::memset(optParam, 0, sizeof(*optParam)); return ULT_OK; } int APS5_VABI sceUltMutexCreate(void* mutex, const char* name, void* waitingQueueResourcePool, const UltMutexOptParam* optParam, std::uint32_t buildVersion) { (void)name; (void)buildVersion; if (mutex == nullptr) { return ULT_ERROR_NULL; } auto state = std::make_shared(); state->_attribute = optParam != nullptr ? optParam->attribute : 0; std::lock_guard lock(gMutex); if (waitingQueueResourcePool != nullptr && gResourcePools.find(waitingQueueResourcePool) == gResourcePools.end()) { return ULT_ERROR_INVALID; } std::memset(mutex, 0, 256); gMutexes[mutex] = std::move(state); return ULT_OK; } int APS5_VABI sceUltMutexLock(void* mutex) { std::shared_ptr state; { std::lock_guard lock(gMutex); auto it = gMutexes.find(mutex); if (it == gMutexes.end()) { return mutex == nullptr ? ULT_ERROR_NULL : ULT_ERROR_STATE; } state = it->second; } state->_mutex.lock(); return ULT_OK; } int APS5_VABI sceUltMutexUnlock(void* mutex) { std::shared_ptr state; { std::lock_guard lock(gMutex); auto it = gMutexes.find(mutex); if (it == gMutexes.end()) { return mutex == nullptr ? ULT_ERROR_NULL : ULT_ERROR_STATE; } state = it->second; } state->_mutex.unlock(); return ULT_OK; } int APS5_VABI sceUltWaitingQueueResourcePoolGetWorkAreaSize(std::uint32_t numThreads, std::uint32_t numSyncObjects) { return static_cast(alignUp(static_cast(numThreads + numSyncObjects) * 256u, 8u)); } int APS5_VABI sceUltWaitingQueueResourcePoolCreate(void* pool, const char* name, std::uint32_t numThreads, std::uint32_t numSyncObjects, void* workArea, const void* optParam, std::uint32_t buildVersion) { (void)name; (void)optParam; (void)buildVersion; if (pool == nullptr) { return ULT_ERROR_NULL; } std::memset(pool, 0, 256); std::lock_guard lock(gMutex); gResourcePools[pool] = {numThreads, numSyncObjects, workArea}; return ULT_OK; } std::uint64_t APS5_VABI sceUltQueueDataResourcePoolGetWorkAreaSize(std::uint32_t numData, std::uint64_t dataSize, std::uint32_t numQueueObject) { const std::uint64_t dataArea = static_cast(numData) * alignUp(dataSize, 8u); const std::uint64_t queueArea = static_cast(numQueueObject) * 512u; return alignUp(dataArea + queueArea, 8u); } int APS5_VABI sceUltQueueDataResourcePoolCreate(void* pool, const char* name, std::uint32_t numData, std::uint64_t dataSize, std::uint32_t numQueueObject, void* waitingQueueResourcePool, void* workArea, const void* optParam, std::uint32_t buildVersion) { (void)name; (void)optParam; (void)buildVersion; if (pool == nullptr) { return ULT_ERROR_NULL; } std::lock_guard lock(gMutex); if (waitingQueueResourcePool != nullptr && gResourcePools.find(waitingQueueResourcePool) == gResourcePools.end()) { return ULT_ERROR_INVALID; } std::memset(pool, 0, 512); gQueueDataPools[pool] = {numData, dataSize, numQueueObject, waitingQueueResourcePool, workArea}; return ULT_OK; } int APS5_VABI sceUltQueueCreate(void* queue, const char* name, std::uint64_t dataSize, void* waitingQueueResourcePool, void* queueDataResourcePool, const void* optParam, std::uint32_t buildVersion) { (void)name; (void)optParam; (void)buildVersion; if (queue == nullptr) { return ULT_ERROR_NULL; } auto state = std::make_shared(); state->_dataSize = dataSize; state->_waitingPool = waitingQueueResourcePool; state->_dataPool = queueDataResourcePool; std::lock_guard lock(gMutex); auto dataPoolIt = gQueueDataPools.find(queueDataResourcePool); if (dataPoolIt == gQueueDataPools.end()) { return ULT_ERROR_INVALID; } if (waitingQueueResourcePool != nullptr && gResourcePools.find(waitingQueueResourcePool) == gResourcePools.end()) { return ULT_ERROR_INVALID; } state->_capacity = dataPoolIt->second._numData; std::memset(queue, 0, 512); gQueues[queue] = std::move(state); return ULT_OK; } int APS5_VABI sceUltQueuePush(void* queue, const void* data) { std::shared_ptr state; if (int ret = queueGetState(queue, &state); ret != ULT_OK) { return ret; } if (data == nullptr && state->_dataSize != 0) { return ULT_ERROR_NULL; } std::lock_guard lock(state->_mutex); if (state->_capacity != 0 && state->_items.size() >= state->_capacity) { return ULT_OK; } auto& item = state->_items.emplace_back(static_cast(state->_dataSize)); if (!item.empty()) { std::memcpy(item.data(), data, item.size()); } return ULT_OK; } int APS5_VABI sceUltQueueTryPop(void* queue, void* data) { std::shared_ptr state; if (int ret = queueGetState(queue, &state); ret != ULT_OK) { return ret; } if (data == nullptr && state->_dataSize != 0) { return ULT_ERROR_NULL; } std::lock_guard lock(state->_mutex); if (state->_items.empty()) { return ULT_ERROR_AGAIN; } auto item = std::move(state->_items.front()); state->_items.pop_front(); if (!item.empty()) { std::memcpy(data, item.data(), item.size()); } return ULT_OK; } int APS5_VABI sceUltSemaphoreCreate(void* semaphore, const char* name, std::int32_t numInitialResource, void* waitingQueueResourcePool, const void* optParam, std::uint32_t buildVersion) { (void)name; (void)optParam; (void)buildVersion; if (semaphore == nullptr) { return ULT_ERROR_NULL; } if ((reinterpret_cast(semaphore) & 7u) != 0) { return ULT_ERROR_ALIGNMENT; } if (numInitialResource < 0) { return ULT_ERROR_RANGE; } auto state = std::make_shared(); state->_resources = numInitialResource; std::lock_guard lock(gMutex); if (waitingQueueResourcePool != nullptr && gResourcePools.find(waitingQueueResourcePool) == gResourcePools.end()) { return ULT_ERROR_INVALID; } if (gSemaphores.find(semaphore) != gSemaphores.end()) { return ULT_ERROR_STATE; } std::memset(semaphore, 0, 256); gSemaphores[semaphore] = std::move(state); return ULT_OK; } int APS5_VABI sceUltSemaphoreAcquire(void* semaphore, std::int32_t numResource) { if (numResource <= 0) { return ULT_ERROR_RANGE; } std::shared_ptr state; if (int ret = semaphoreGetState(semaphore, &state); ret != ULT_OK) { return ret; } std::unique_lock lock(state->_mutex); if (!state->_alive) { return ULT_ERROR_STATE; } state->_waiters++; state->_available.wait(lock, [&] { return !state->_alive || state->_resources >= numResource; }); state->_waiters--; if (!state->_alive) { return ULT_ERROR_STATE; } state->_resources -= numResource; return ULT_OK; } int APS5_VABI sceUltSemaphoreTryAcquire(void* semaphore, std::int32_t numResource) { if (numResource <= 0) { return ULT_ERROR_RANGE; } std::shared_ptr state; if (int ret = semaphoreGetState(semaphore, &state); ret != ULT_OK) { return ret; } std::lock_guard lock(state->_mutex); if (!state->_alive) { return ULT_ERROR_STATE; } if (state->_resources < numResource) { return ULT_ERROR_AGAIN; } state->_resources -= numResource; return ULT_OK; } int APS5_VABI sceUltSemaphoreRelease(void* semaphore, std::int32_t numResource) { if (numResource <= 0) { return ULT_ERROR_RANGE; } std::shared_ptr state; if (int ret = semaphoreGetState(semaphore, &state); ret != ULT_OK) { return ret; } { std::lock_guard lock(state->_mutex); if (!state->_alive) { return ULT_ERROR_STATE; } if (state->_resources > INT32_MAX - numResource) { return ULT_ERROR_RANGE; } state->_resources += numResource; } state->_available.notify_all(); return ULT_OK; } int APS5_VABI sceUltSemaphoreDestroy(void* semaphore) { if (semaphore == nullptr) { return ULT_ERROR_NULL; } std::lock_guard globalLock(gMutex); auto it = gSemaphores.find(semaphore); if (it == gSemaphores.end()) { return ULT_ERROR_STATE; } auto state = it->second; std::lock_guard stateLock(state->_mutex); if (state->_waiters != 0) { return ULT_ERROR_BUSY; } state->_alive = false; gSemaphores.erase(it); return ULT_OK; } int APS5_VABI sceUltUlthreadRuntimeOptParamInitialize(UltUlthreadRuntimeOptParam* optParam, std::uint32_t buildVersion) { (void)buildVersion; if (optParam == nullptr) { return ULT_ERROR_NULL; } std::memset(optParam, 0, sizeof(*optParam)); return ULT_OK; } std::uint64_t APS5_VABI sceUltUlthreadRuntimeGetWorkAreaSize(std::uint32_t maxNumUlthread, std::uint32_t numWorkerThread) { return alignUp(static_cast(maxNumUlthread) * 256u + static_cast(numWorkerThread) * 16u * 1024u, 8u); } int APS5_VABI sceUltUlthreadRuntimeCreate(void* runtime, const char* name, std::uint32_t maxNumUlthread, std::uint32_t numWorkerThread, void* workArea, const void* optParam, std::uint32_t buildVersion) { (void)name; (void)optParam; (void)buildVersion; if (runtime == nullptr) { return ULT_ERROR_NULL; } std::memset(runtime, 0, 4096); std::lock_guard lock(gMutex); gRuntimes[runtime] = {maxNumUlthread, numWorkerThread, workArea}; return ULT_OK; } int APS5_VABI sceUltUlthreadCreate(void* ulthread, const char* name, UltUlthreadEntry entry, std::uint64_t arg, void* context, std::uint64_t sizeContext, void* runtime, const void* optParam, std::uint32_t buildVersion) { (void)context; (void)sizeContext; (void)optParam; (void)buildVersion; if (ulthread == nullptr || entry == nullptr || runtime == nullptr) { return ULT_ERROR_NULL; } auto state = std::make_shared(); state->_entry = entry; state->_arg = arg; { std::lock_guard lock(gMutex); if (gRuntimes.find(runtime) == gRuntimes.end()) { return ULT_ERROR_STATE; } if (gUlthreads.find(ulthread) != gUlthreads.end()) { return ULT_ERROR_STATE; } std::memset(ulthread, 0, 512); gUlthreads[ulthread] = state; } const int result = scePthreadCreate(&state->_thread, nullptr, ulthreadRunner, state.get(), name != nullptr ? name : ""); if (result != 0) { std::lock_guard lock(gMutex); gUlthreads.erase(ulthread); return ULT_ERROR_AGAIN; } return ULT_OK; } int APS5_VABI sceUltUlthreadJoin(void* ulthread, std::int32_t* status) { if (ulthread == nullptr) { return ULT_ERROR_NULL; } std::shared_ptr state; { std::lock_guard lock(gMutex); auto it = gUlthreads.find(ulthread); if (it == gUlthreads.end()) { return ULT_ERROR_STATE; } state = it->second; } void* result = nullptr; if (scePthreadJoin(state->_thread, &result) != 0) { return ULT_ERROR_STATE; } if (status != nullptr) { *status = static_cast(reinterpret_cast(result)); } std::lock_guard lock(gMutex); gUlthreads.erase(ulthread); return ULT_OK; } }