Merge pull request #1705 from muhzain05/fix/ult-finalize-busy
fix(ult): reject finalize with outstanding ulthreads
This commit is contained in:
commit
cb6d833295
|
|
@ -15,4 +15,17 @@ if(BUILD_TESTING)
|
|||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceUlt>")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
add_test_executable(guest_ult_thread_tests ${LIBS_INCLUDE_DIR}/tests/GuestUltThread.cpp)
|
||||
target_include_directories(guest_ult_thread_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_ult_thread_tests PRIVATE libSceUlt libc)
|
||||
configure_windows_unwind(guest_ult_thread_tests)
|
||||
foreach(testCase IN ITEMS outstanding validation)
|
||||
add_test(NAME guest_ult_thread_${testCase} COMMAND guest_ult_thread_tests ${testCase})
|
||||
set_tests_properties(guest_ult_thread_${testCase} PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_ult_thread_${testCase} PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceUlt>")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -123,6 +123,9 @@ int APS5_VABI sceUltFinalize() {
|
|||
std::vector<std::shared_ptr<UltSemaphoreState>> semaphores;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(gMutex);
|
||||
if (!gUlthreads.empty()) {
|
||||
return ULT_ERROR_BUSY;
|
||||
}
|
||||
semaphores.reserve(gSemaphores.size());
|
||||
for (auto& entry : gSemaphores) {
|
||||
auto& state = entry.second;
|
||||
|
|
@ -143,7 +146,6 @@ int APS5_VABI sceUltFinalize() {
|
|||
gQueues.clear();
|
||||
gQueueDataPools.clear();
|
||||
gRuntimes.clear();
|
||||
gUlthreads.clear();
|
||||
}
|
||||
for (const auto& state : semaphores) {
|
||||
state->_available.notify_all();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI sceUltInitialize();
|
||||
int APS5_VABI sceUltFinalize();
|
||||
std::uint64_t APS5_VABI sceUltUlthreadRuntimeGetWorkAreaSize(std::uint32_t, std::uint32_t);
|
||||
int APS5_VABI sceUltUlthreadRuntimeCreate(void*, const char*, std::uint32_t, std::uint32_t, void*, const void*, std::uint32_t);
|
||||
int APS5_VABI sceUltUlthreadCreate(void*, const char*, UltUlthreadEntry, std::uint64_t, void*, std::uint64_t, void*, const void*, std::uint32_t);
|
||||
int APS5_VABI sceUltUlthreadJoin(void*, std::int32_t*);
|
||||
}
|
||||
|
||||
static constexpr int Ok = 0;
|
||||
static constexpr int Null = -2139029503;
|
||||
static constexpr int State = -2139029498;
|
||||
static constexpr int Busy = -2139029497;
|
||||
|
||||
static void Require(bool value, int line) {
|
||||
if (!value) {
|
||||
std::fprintf(stderr, "ULT thread check failed at line %d\n", line);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK(value) Require((value), __LINE__)
|
||||
|
||||
static std::atomic<bool> gStarted{false};
|
||||
static std::atomic<bool> gRelease{false};
|
||||
|
||||
static std::int32_t APS5_VABI Parked(std::uint64_t arg) {
|
||||
gStarted.store(true, std::memory_order_release);
|
||||
while (!gRelease.load(std::memory_order_acquire)) std::this_thread::yield();
|
||||
return static_cast<std::int32_t>(arg);
|
||||
}
|
||||
|
||||
static std::int32_t APS5_VABI Immediate(std::uint64_t arg) {
|
||||
return static_cast<std::int32_t>(arg);
|
||||
}
|
||||
|
||||
struct alignas(8) Runtime { std::uint8_t bytes[4096]{}; };
|
||||
struct alignas(8) Thread { std::uint8_t bytes[512]{}; };
|
||||
|
||||
static void Outstanding() {
|
||||
Runtime runtime;
|
||||
Thread thread;
|
||||
CHECK(sceUltUlthreadRuntimeCreate(&runtime, "runtime", 1, 1, nullptr, nullptr, 0) == Ok);
|
||||
CHECK(sceUltUlthreadCreate(&thread, "parked", Parked, 0x5A, nullptr, 0, &runtime, nullptr, 0) == Ok);
|
||||
while (!gStarted.load(std::memory_order_acquire)) std::this_thread::yield();
|
||||
|
||||
const int outstanding = sceUltFinalize();
|
||||
gRelease.store(true, std::memory_order_release);
|
||||
std::int32_t status = 0;
|
||||
const int joined = sceUltUlthreadJoin(&thread, &status);
|
||||
|
||||
CHECK(outstanding == Busy);
|
||||
CHECK(joined == Ok && status == 0x5A);
|
||||
CHECK(sceUltUlthreadJoin(&thread, &status) == State);
|
||||
CHECK(sceUltFinalize() == Ok);
|
||||
}
|
||||
|
||||
static void Validation() {
|
||||
Runtime runtime;
|
||||
Thread thread;
|
||||
gStarted.store(false, std::memory_order_release);
|
||||
gRelease.store(false, std::memory_order_release);
|
||||
CHECK(sceUltUlthreadRuntimeCreate(nullptr, "runtime", 1, 1, nullptr, nullptr, 0) == Null);
|
||||
CHECK(sceUltUlthreadRuntimeCreate(&runtime, "runtime", 1, 1, nullptr, nullptr, 0) == Ok);
|
||||
CHECK(sceUltUlthreadCreate(nullptr, "thread", Immediate, 0, nullptr, 0, &runtime, nullptr, 0) == Null);
|
||||
CHECK(sceUltUlthreadCreate(&thread, "thread", nullptr, 0, nullptr, 0, &runtime, nullptr, 0) == Null);
|
||||
CHECK(sceUltUlthreadCreate(&thread, "thread", Immediate, 0, nullptr, 0, nullptr, nullptr, 0) == Null);
|
||||
CHECK(sceUltUlthreadJoin(nullptr, nullptr) == Null);
|
||||
CHECK(sceUltUlthreadJoin(&thread, nullptr) == State);
|
||||
|
||||
CHECK(sceUltUlthreadCreate(&thread, "thread", Immediate, 0x27, nullptr, 0, &runtime, nullptr, 0) == Ok);
|
||||
CHECK(sceUltUlthreadCreate(&thread, "thread", Immediate, 0x27, nullptr, 0, &runtime, nullptr, 0) == State);
|
||||
CHECK(sceUltFinalize() == Busy);
|
||||
std::int32_t status = 0;
|
||||
CHECK(sceUltUlthreadJoin(&thread, &status) == Ok && status == 0x27);
|
||||
CHECK(sceUltFinalize() == Ok);
|
||||
CHECK(sceUltUlthreadRuntimeGetWorkAreaSize(1, 1) == 256u + 16u * 1024u);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
CHECK(argc == 2);
|
||||
CHECK(sceUltInitialize() == Ok);
|
||||
if (std::strcmp(argv[1], "outstanding") == 0) Outstanding();
|
||||
else if (std::strcmp(argv[1], "validation") == 0) Validation();
|
||||
else CHECK(false);
|
||||
}
|
||||
Loading…
Reference in New Issue