mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
feat(libSceUlt): implement sceUltMutexDestroy and sceUltWaitingQueueResourcePoolDestroy
This commit is contained in:
1 parent
dacd78525e
commit
ca9f4e47b1
4 files changed
+152
-3
No files matched your search
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
@@ -123,6 +124,7 @@ int APS5_VABI sceUltInitialize() {
|
||||
|
||||
int APS5_VABI sceUltFinalize() {
|
||||
std::vector<std::shared_ptr<UltSemaphoreState>> semaphores;
|
||||
std::vector<std::shared_ptr<UltMutexState>> mutexes;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(gMutex);
|
||||
if (!gUlthreads.empty()) {
|
||||
@@ -136,6 +138,13 @@ int APS5_VABI sceUltFinalize() {
|
||||
semaphores.push_back(state);
|
||||
}
|
||||
gSemaphores.clear();
|
||||
mutexes.reserve(gMutexes.size());
|
||||
for (auto& entry : gMutexes) {
|
||||
auto& state = entry.second;
|
||||
std::lock_guard<std::mutex> stateLock(state->_mutex);
|
||||
state->_alive = false;
|
||||
mutexes.push_back(state);
|
||||
}
|
||||
gMutexes.clear();
|
||||
gResourcePools.clear();
|
||||
for (auto& entry : gQueues) {
|
||||
@@ -152,6 +161,9 @@ int APS5_VABI sceUltFinalize() {
|
||||
for (const auto& state : semaphores) {
|
||||
state->_available.notify_all();
|
||||
}
|
||||
for (const auto& state : mutexes) {
|
||||
state->_available.notify_all();
|
||||
}
|
||||
return ULT_OK;
|
||||
}
|
||||
|
||||
@@ -172,15 +184,38 @@ int APS5_VABI sceUltMutexCreate(void* mutex, const char* name, void* waitingQueu
|
||||
}
|
||||
auto state = std::make_shared<UltMutexState>();
|
||||
state->_attribute = optParam != nullptr ? optParam->attribute : 0;
|
||||
state->_waitingPool = waitingQueueResourcePool;
|
||||
std::lock_guard<std::mutex> lock(gMutex);
|
||||
if (waitingQueueResourcePool != nullptr && gResourcePools.find(waitingQueueResourcePool) == gResourcePools.end()) {
|
||||
return ULT_ERROR_INVALID;
|
||||
}
|
||||
if (gMutexes.find(mutex) != gMutexes.end()) {
|
||||
return ULT_ERROR_STATE;
|
||||
}
|
||||
std::memset(mutex, 0, 256);
|
||||
gMutexes[mutex] = std::move(state);
|
||||
return ULT_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceUltMutexDestroy(void* mutex) {
|
||||
if (mutex == nullptr) {
|
||||
return ULT_ERROR_NULL;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(gMutex);
|
||||
auto it = gMutexes.find(mutex);
|
||||
if (it == gMutexes.end()) {
|
||||
return ULT_ERROR_STATE;
|
||||
}
|
||||
auto state = it->second;
|
||||
std::lock_guard<std::mutex> stateLock(state->_mutex);
|
||||
if (state->_lockCount != 0 || state->_waiters != 0) {
|
||||
return ULT_ERROR_BUSY;
|
||||
}
|
||||
state->_alive = false;
|
||||
gMutexes.erase(it);
|
||||
return ULT_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceUltMutexLock(void* mutex) {
|
||||
std::shared_ptr<UltMutexState> state;
|
||||
{
|
||||
@@ -191,7 +226,30 @@ int APS5_VABI sceUltMutexLock(void* mutex) {
|
||||
}
|
||||
state = it->second;
|
||||
}
|
||||
state->_mutex.lock();
|
||||
const auto self = std::this_thread::get_id();
|
||||
std::unique_lock<std::mutex> lock(state->_mutex);
|
||||
if (!state->_alive) {
|
||||
return ULT_ERROR_STATE;
|
||||
}
|
||||
if (state->_owner == self) {
|
||||
++state->_lockCount;
|
||||
return ULT_OK;
|
||||
}
|
||||
++state->_waiters;
|
||||
try {
|
||||
state->_available.wait(lock, [&] {
|
||||
return !state->_alive || state->_lockCount == 0;
|
||||
});
|
||||
} catch (...) {
|
||||
--state->_waiters;
|
||||
throw;
|
||||
}
|
||||
--state->_waiters;
|
||||
if (!state->_alive) {
|
||||
return ULT_ERROR_STATE;
|
||||
}
|
||||
state->_owner = self;
|
||||
state->_lockCount = 1;
|
||||
return ULT_OK;
|
||||
}
|
||||
|
||||
@@ -205,7 +263,16 @@ int APS5_VABI sceUltMutexUnlock(void* mutex) {
|
||||
}
|
||||
state = it->second;
|
||||
}
|
||||
state->_mutex.unlock();
|
||||
const auto self = std::this_thread::get_id();
|
||||
std::lock_guard<std::mutex> lock(state->_mutex);
|
||||
if (!state->_alive || state->_owner != self) {
|
||||
return ULT_ERROR_STATE;
|
||||
}
|
||||
--state->_lockCount;
|
||||
if (state->_lockCount == 0) {
|
||||
state->_owner = {};
|
||||
state->_available.notify_one();
|
||||
}
|
||||
return ULT_OK;
|
||||
}
|
||||
|
||||
@@ -226,6 +293,43 @@ int APS5_VABI sceUltWaitingQueueResourcePoolCreate(void* pool, const char* name,
|
||||
return ULT_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceUltWaitingQueueResourcePoolDestroy(void* pool) {
|
||||
if (pool == nullptr) {
|
||||
return ULT_ERROR_NULL;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(gMutex);
|
||||
auto it = gResourcePools.find(pool);
|
||||
if (it == gResourcePools.end()) {
|
||||
return ULT_ERROR_STATE;
|
||||
}
|
||||
for (const auto& entry : gMutexes) {
|
||||
if (entry.second->_waitingPool == pool) {
|
||||
std::lock_guard<std::mutex> stateLock(entry.second->_mutex);
|
||||
if (entry.second->_waiters != 0) {
|
||||
return ULT_ERROR_BUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& entry : gSemaphores) {
|
||||
if (entry.second->_waitingPool == pool) {
|
||||
std::lock_guard<std::mutex> stateLock(entry.second->_mutex);
|
||||
if (entry.second->_waiters != 0) {
|
||||
return ULT_ERROR_BUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& entry : gQueues) {
|
||||
if (entry.second->_pool && entry.second->_pool->_waitingPool == pool) {
|
||||
std::lock_guard<std::mutex> poolLock(entry.second->_pool->_mutex);
|
||||
if (entry.second->_waiters != 0) {
|
||||
return ULT_ERROR_BUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
gResourcePools.erase(it);
|
||||
return ULT_OK;
|
||||
}
|
||||
|
||||
std::uint64_t APS5_VABI sceUltQueueDataResourcePoolGetWorkAreaSize(std::uint32_t numData, std::uint64_t dataSize, std::uint32_t numQueueObject) {
|
||||
if (dataSize > UINT64_MAX - 7u) throw std::out_of_range("ULT queue data size overflow");
|
||||
const auto stride = alignUp(dataSize, 8u);
|
||||
@@ -360,6 +464,7 @@ int APS5_VABI sceUltSemaphoreCreate(void* semaphore, const char* name, std::int3
|
||||
}
|
||||
auto state = std::make_shared<UltSemaphoreState>();
|
||||
state->_resources = numInitialResource;
|
||||
state->_waitingPool = waitingQueueResourcePool;
|
||||
std::lock_guard<std::mutex> lock(gMutex);
|
||||
if (waitingQueueResourcePool != nullptr && gResourcePools.find(waitingQueueResourcePool) == gResourcePools.end()) {
|
||||
return ULT_ERROR_INVALID;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "SceTypes.hpp"
|
||||
|
||||
@@ -19,8 +20,14 @@ constexpr int ULT_ERROR_BUSY = -2139029497;
|
||||
constexpr int ULT_ERROR_AGAIN = -2139029496;
|
||||
|
||||
struct UltMutexState {
|
||||
std::recursive_mutex _mutex;
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _available;
|
||||
std::thread::id _owner{};
|
||||
std::uint32_t _lockCount = 0;
|
||||
std::uint32_t _waiters = 0;
|
||||
bool _alive = true;
|
||||
std::uint32_t _attribute = 0;
|
||||
void* _waitingPool = nullptr;
|
||||
};
|
||||
|
||||
struct UltSemaphoreState {
|
||||
@@ -29,6 +36,7 @@ struct UltSemaphoreState {
|
||||
std::int32_t _resources = 0;
|
||||
std::uint32_t _waiters = 0;
|
||||
bool _alive = true;
|
||||
void* _waitingPool = nullptr;
|
||||
};
|
||||
|
||||
struct UltResourcePoolState {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
extern "C" {
|
||||
@@ -30,6 +31,11 @@ int APS5_VABI _sceUltQueueDataResourcePoolCreate(void*, const char*, std::uint32
|
||||
int APS5_VABI _sceUltQueueCreate(void*, const char*, std::uint64_t, void*, void*, const void*, std::uint32_t);
|
||||
int APS5_VABI sceUltQueuePush(void*, const void*);
|
||||
int APS5_VABI sceUltQueuePop(void*, void*);
|
||||
int APS5_VABI sceUltMutexDestroy(void*);
|
||||
int APS5_VABI sceUltWaitingQueueResourcePoolDestroy(void*);
|
||||
int APS5_VABI sceUltSemaphoreDestroy(void*);
|
||||
int APS5_VABI sceUltQueueDestroy(void*);
|
||||
int APS5_VABI sceUltQueueDataResourcePoolDestroy(void*);
|
||||
}
|
||||
|
||||
static constexpr int Ok = 0;
|
||||
@@ -177,6 +183,34 @@ static void UnderscoredCreate() {
|
||||
std::uint64_t value = 0x1122334455667788ull, output = 0;
|
||||
CHECK(sceUltQueuePush(queue, &value) == Ok);
|
||||
CHECK(sceUltQueuePop(queue, &output) == Ok && output == value);
|
||||
CHECK(sceUltWaitingQueueResourcePoolDestroy(nullptr) == Null);
|
||||
CHECK(sceUltWaitingQueueResourcePoolDestroy(unknownPool) == State);
|
||||
CHECK(sceUltMutexDestroy(nullptr) == Null);
|
||||
CHECK(sceUltMutexLock(mutex) == Ok);
|
||||
CHECK(sceUltMutexDestroy(mutex) == Busy);
|
||||
std::atomic<bool> waiterStarted{false};
|
||||
std::atomic<bool> waiterAcquired{false};
|
||||
std::thread waiter([&] {
|
||||
waiterStarted.store(true, std::memory_order_release);
|
||||
CHECK(sceUltMutexLock(mutex) == Ok);
|
||||
waiterAcquired.store(true, std::memory_order_release);
|
||||
CHECK(sceUltMutexUnlock(mutex) == Ok);
|
||||
});
|
||||
while (!waiterStarted.load(std::memory_order_acquire)) std::this_thread::yield();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
CHECK(!waiterAcquired.load(std::memory_order_acquire));
|
||||
CHECK(sceUltMutexDestroy(mutex) == Busy);
|
||||
CHECK(sceUltWaitingQueueResourcePoolDestroy(pool) == Busy);
|
||||
CHECK(sceUltMutexUnlock(mutex) == Ok);
|
||||
waiter.join();
|
||||
CHECK(waiterAcquired.load(std::memory_order_acquire));
|
||||
CHECK(sceUltWaitingQueueResourcePoolDestroy(pool) == Ok);
|
||||
CHECK(sceUltWaitingQueueResourcePoolDestroy(pool) == State);
|
||||
CHECK(sceUltMutexDestroy(mutex) == Ok);
|
||||
CHECK(sceUltMutexDestroy(mutex) == State);
|
||||
CHECK(sceUltSemaphoreDestroy(semaphore) == Ok);
|
||||
CHECK(sceUltQueueDestroy(queue) == Ok);
|
||||
CHECK(sceUltQueueDataResourcePoolDestroy(dataPool) == Ok);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -9,3 +9,5 @@
|
||||
### Functional
|
||||
|
||||
- [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) use preallocated host storage instead of the supplied guest work area; its existing size formula and the 512-byte object layouts remain unverified. Waiting-queue pool thread limits and waiter priority ordering are not implemented. Finalization wakes blocked queue callers with `ULT_ERROR_STATE`.
|
||||
- [sceUltWaitingQueueResourcePoolDestroy](../../../core/libs/prx/libSceUlt/Export.cpp) refuses with `ULT_ERROR_BUSY` only when a thread is currently waiting on a pool-backed mutex, semaphore or queue, matching the PS5 libSceUlt disassembly (0x473a in or55417wcDk); bound but idle objects do not block pool destruction. Waiting-queue pool guest work area node accounting is not implemented.
|
||||
- [sceUltMutexDestroy](../../../core/libs/prx/libSceUlt/Export.cpp) returns `ULT_ERROR_BUSY` when the mutex is held or has waiting threads, tracked via owner and waiter counts in `UltMutexState`. Prosper (`hle_ult.cpp`) logs a destroy of a held mutex and returns `ULT_OK`; AnyPS5 returns `ULT_ERROR_BUSY` to prevent destroying active mutexes.
|
||||
Reference in new issue
Block a user