Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions core/libs/prx/libkernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_pthread_mutexattr PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_pthread_destroyed_objects_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadDestroyedObjects.cpp)
target_include_directories(guest_pthread_destroyed_objects_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_pthread_destroyed_objects_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_pthread_destroyed_objects_tests)
add_test(NAME guest_pthread_destroyed_objects COMMAND guest_pthread_destroyed_objects_tests)
set_tests_properties(guest_pthread_destroyed_objects PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_pthread_destroyed_objects PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_pthread_mutex_errors_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadMutexErrors.cpp)
target_include_directories(guest_pthread_mutex_errors_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_pthread_mutex_errors_tests PRIVATE libkernel libc)
Expand Down
23 changes: 18 additions & 5 deletions core/libs/prx/libkernel/Pthread/src/Cond.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ std::shared_ptr<TimedWait::Condition> resolveCond(PthreadCond* cond, int* clockI
std::atomic_ref<PthreadCond> slot(*cond);
auto current = slot.load(std::memory_order_acquire);
if (current == destroyedCond())
throw std::runtime_error("Condition variable has been destroyed");
return nullptr;
if (!current) {
current = new PthreadCondPrivate();
slot.store(current, std::memory_order_release);
Expand Down Expand Up @@ -84,6 +84,8 @@ int finishWait(std::shared_ptr<TimedWait::Condition>& condition, bool timedOut)
}

int waitUntil(std::shared_ptr<TimedWait::Condition> condition, PthreadMutex* mutex, std::optional<std::uint64_t> deadlineNanos, const void* caller) {
if (!condition)
return SCE_KERNEL_ERROR_EINVAL;
PthreadMutex m = nullptr;
if (const int error = mutexOwnership(mutex, &m))
return error;
Expand Down Expand Up @@ -124,6 +126,8 @@ int CondOperations::AbsoluteTimedwait(PthreadCond* cond, PthreadMutex* mutex, co
KernelUseconds usec = 0;
int clockId = 0;
auto condition = resolveCond(cond, &clockId);
if (!condition)
return SCE_KERNEL_ERROR_EINVAL;
if (!PosixThread::RelativeMicroseconds(clockId, abstime, &usec))
throw std::invalid_argument("Invalid absolute condition variable timeout");
return waitUntil(std::move(condition), mutex, TimedWait::DeadlineNanos(usec), __builtin_return_address(0));
Expand Down Expand Up @@ -167,25 +171,34 @@ int APS5_VABI scePthreadCondDestroy(PthreadCond* cond) {
throw std::invalid_argument("Condition variable pointer is null");
std::lock_guard lock(condInitializationMutex);
if (*cond == destroyedCond())
throw std::runtime_error("Condition variable has already been destroyed");
return SCE_KERNEL_ERROR_EINVAL;
delete *cond;
std::atomic_ref<PthreadCond>(*cond).store(destroyedCond(), std::memory_order_release);
return 0;
}

int APS5_VABI scePthreadCondSignal(PthreadCond* cond) {
resolveCond(cond)->NotifyOne();
const auto resolved = resolveCond(cond);
if (!resolved)
return SCE_KERNEL_ERROR_EINVAL;
resolved->NotifyOne();
return 0;
}

int APS5_VABI scePthreadCondBroadcast(PthreadCond* cond) {
resolveCond(cond)->NotifyAll();
const auto resolved = resolveCond(cond);
if (!resolved)
return SCE_KERNEL_ERROR_EINVAL;
resolved->NotifyAll();
return 0;
}

int APS5_VABI scePthreadCondSignalto(PthreadCond* cond, Pthread thread) {
(void)thread;
resolveCond(cond)->NotifyAll();
const auto resolved = resolveCond(cond);
if (!resolved)
return SCE_KERNEL_ERROR_EINVAL;
resolved->NotifyAll();
return 0;
}

Expand Down
6 changes: 5 additions & 1 deletion core/libs/prx/libkernel/Pthread/src/Mutex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ PthreadMutex resolveMutex(PthreadMutex* mutex, bool initialize) {
std::lock_guard lock(initializationMutex);
const auto current = slot.load(std::memory_order_acquire);
if (current == destroyedMutex())
throw std::runtime_error("Mutex has been destroyed");
return nullptr;
if (current && current != adaptiveInitializer())
return current;
if (!initialize)
Expand All @@ -53,6 +53,8 @@ PthreadMutex resolveMutex(PthreadMutex* mutex, bool initialize) {

template<typename TAcquire>
int acquireMutex(PthreadMutex mutex, TAcquire acquire, int unavailable, bool tryOnly) {
if (!mutex)
return SCE_KERNEL_ERROR_EINVAL;
const auto thread = std::this_thread::get_id();
const bool owned = mutex->_owner.load(std::memory_order_acquire) == thread;
if (owned && mutex->_type != MutexType::Recursive) {
Expand Down Expand Up @@ -188,6 +190,8 @@ int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex) {

int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex) {
auto* current = resolveMutex(mutex, false);
if (!current)
return SCE_KERNEL_ERROR_EINVAL;
if (current->_owner.load(std::memory_order_acquire) != std::this_thread::get_id())
return sceNotPermitted;
bool released = true;
Expand Down
46 changes: 46 additions & 0 deletions core/libs/tests/GuestPthreadDestroyedObjects.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "SceTypes.hpp"
#include <cstdint>
#include <cstdlib>

extern "C" {
int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char* name);
int APS5_VABI scePthreadMutexDestroy(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexTrylock(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex);
int APS5_VABI scePthreadCondInit(PthreadCond* cond, const PthreadCondattr* attr, const char* name);
int APS5_VABI scePthreadCondDestroy(PthreadCond* cond);
int APS5_VABI scePthreadCondSignal(PthreadCond* cond);
int APS5_VABI scePthreadCondBroadcast(PthreadCond* cond);
int APS5_VABI scePthreadCondWait(PthreadCond* cond, PthreadMutex* mutex);
int APS5_VABI pthread_mutex_lock_nid_postfix(PthreadMutex* mutex);
int APS5_VABI pthread_cond_signal_nid_postfix(PthreadCond* cond);
int APS5_VABI pthread_cond_destroy_nid_postfix(PthreadCond* cond);
}

static constexpr int SCE_OK = 0;
static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast<int>(0x80020016u);
static constexpr int POSIX_EINVAL = 22;

static void Require(bool value) { if (!value) std::abort(); }

int main() {
PthreadMutex mutex = nullptr;
Require(scePthreadMutexInit(&mutex, nullptr, "destroyed") == SCE_OK);
Require(scePthreadMutexDestroy(&mutex) == SCE_OK);
Require(scePthreadMutexLock(&mutex) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadMutexTrylock(&mutex) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadMutexUnlock(&mutex) == SCE_KERNEL_ERROR_EINVAL);
Require(pthread_mutex_lock_nid_postfix(&mutex) == POSIX_EINVAL);
Require(mutex == reinterpret_cast<PthreadMutex>(std::uintptr_t{2}));

PthreadCond cond = nullptr;
Require(scePthreadCondInit(&cond, nullptr, "destroyed") == SCE_OK);
Require(scePthreadCondDestroy(&cond) == SCE_OK);
Require(scePthreadCondSignal(&cond) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadCondBroadcast(&cond) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadCondWait(&cond, &mutex) == SCE_KERNEL_ERROR_EINVAL);
Require(pthread_cond_signal_nid_postfix(&cond) == POSIX_EINVAL);
Require(scePthreadCondDestroy(&cond) == SCE_KERNEL_ERROR_EINVAL);
Require(pthread_cond_destroy_nid_postfix(&cond) == POSIX_EINVAL);
}
Loading