Files
AnyPS5/core/libs/tests/GuestKernelErrors.cpp
T
MaxandClaude Opus 5.5 55ff5484d6 fix(libkernel): time out timed locks of a lock the caller already holds
scePthreadMutexTimedlock on a mutex the caller holds, and
scePthreadRwlockTimedrdlock and scePthreadRwlockTimedwrlock on a rwlock
it holds for writing, returned EDEADLK at once. The console returns
ETIMEDOUT, as the prosper console oracle measured. They now sleep until
the timeout and return ETIMEDOUT, as FreeBSD libthr does. The POSIX
timed locks call these and change with them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 02:15:31 +02:00

495 lines
27 KiB
C++

#include "prx/libkernel/KernelErrors.hpp"
#include "prx/libkernel/include/Api.hpp"
#include "prx/libkernel/Semaphore/include/Semaphore.hpp"
#include "prx/libkernel/Pthread/include/Mutex.hpp"
#include "prx/libkernel/Pthread/include/Rwlock.hpp"
#include "prx/libSceSaveData/SaveDataCore.hpp"
#include "SceTypes.hpp"
#include <atomic>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <stdexcept>
#include <thread>
extern "C" {
int APS5_Test_GetEventFlagWaiterCount(KernelEventFlag handle);
}
static constexpr int SCE_OK = 0;
static constexpr std::uint32_t EVF_ATTR_SINGLE = 0x10;
static constexpr std::uint32_t EVF_ATTR_MULTI = 0x20;
static constexpr std::uint32_t EVF_ATTR_SHARED = 0x100;
static constexpr std::uint32_t EVF_WAITMODE_AND = 0x01;
static constexpr std::uint32_t EVF_WAITMODE_OR = 0x02;
static constexpr std::uint32_t EVF_WAITMODE_CLEAR_ALL = 0x10;
static constexpr std::uint32_t EVF_WAITMODE_CLEAR_PAT = 0x20;
static constexpr int MUTEX_TYPE_ERRORCHECK = 1;
static constexpr int MUTEX_TYPE_NORMAL = 3;
static constexpr int PRIO_NONE = 0;
static constexpr int PRIO_INHERIT = 1;
static constexpr int PRIO_PROTECT = 2;
static constexpr int MUTEX_TYPE_ADAPTIVE = 4;
static constexpr int POSIX_EDEADLK = 11;
static void Require(bool value) { if (!value) std::abort(); }
static void WaitForEventFlagWaiters(KernelEventFlag handle, int expected) {
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
while (APS5_Test_GetEventFlagWaiterCount(handle) != expected) {
if (std::chrono::steady_clock::now() >= deadline) Require(false);
std::this_thread::yield();
}
}
template <typename TLock>
static void RequireTimesOut(TLock lock) {
const auto start = std::chrono::steady_clock::now();
Require(lock(20000) == SCE_KERNEL_ERROR_ETIMEDOUT);
Require(std::chrono::steady_clock::now() - start >= std::chrono::milliseconds(15));
}
int main() {
constexpr int maximum = std::numeric_limits<int>::max();
KernelSema semaphore = nullptr;
Require(sceKernelCreateSema(&semaphore, "overflow", 1, maximum - 1, maximum, nullptr) == SCE_OK);
Require(sceKernelSignalSema(semaphore, 2) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelSignalSema(semaphore, maximum) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelPollSema(semaphore, maximum - 1) == SCE_OK);
Require(sceKernelSignalSema(semaphore, maximum) == SCE_OK);
Require(sceKernelSignalSema(semaphore, 1) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelPollSema(semaphore, maximum) == SCE_OK);
Require(sceKernelDeleteSema(semaphore) == SCE_OK);
Require(sceKernelCreateSema(&semaphore, "limit", 1, 1, 3, nullptr) == SCE_OK);
Require(sceKernelSignalSema(semaphore, 3) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelSignalSema(semaphore, 2) == SCE_OK);
Require(sceKernelSignalSema(semaphore, 0) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelPollSema(semaphore, 3) == SCE_OK);
Require(sceKernelSignalSema(semaphore, 0) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelPollSema(semaphore, 1) == SCE_KERNEL_ERROR_EBUSY);
Require(sceKernelDeleteSema(semaphore) == SCE_OK);
KernelEqueue eq = 0;
Require(sceKernelCreateEqueue(&eq, "errors") == SCE_OK);
KernelEvent event{};
int count = -1;
const KernelUseconds timeout = 1000;
Require(sceKernelWaitEqueue(eq, &event, 1, &count, &timeout) == SCE_KERNEL_ERROR_ETIMEDOUT);
Require(count == 0);
Require(sceKernelWaitEqueue(eq, nullptr, 1, &count, &timeout) == SCE_KERNEL_ERROR_EFAULT);
Require(sceKernelWaitEqueue(eq, &event, 0, &count, &timeout) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelDeleteUserEvent(eq, 7) == SCE_KERNEL_ERROR_ENOENT);
Require(sceKernelDeleteEqueue(eq) == SCE_OK);
Require(sceKernelDeleteEqueue(eq) == SCE_KERNEL_ERROR_EBADF);
Require(sceKernelWaitEqueue(eq, &event, 1, &count, &timeout) == SCE_KERNEL_ERROR_EBADF);
Require(sceKernelCreateEqueue(nullptr, "errors") == SCE_KERNEL_ERROR_EINVAL);
PthreadMutexattr attr = nullptr;
Require(scePthreadMutexattrInit(&attr) == SCE_OK);
Require(scePthreadMutexattrSettype(&attr, MUTEX_TYPE_ERRORCHECK) == SCE_OK);
Require(scePthreadMutexattrSettype(&attr, 0) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadMutexattrSettype(&attr, 5) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadMutexattrSetprotocol(&attr, PRIO_NONE) == SCE_OK);
Require(scePthreadMutexattrSetprotocol(&attr, PRIO_INHERIT) == SCE_OK);
Require(scePthreadMutexattrSetprotocol(&attr, PRIO_PROTECT) == SCE_OK);
PthreadMutex mutex = nullptr;
Require(scePthreadMutexInit(&mutex, &attr, nullptr) == SCE_OK);
Require(scePthreadMutexattrDestroy(&attr) == SCE_OK);
Require(scePthreadMutexLock(&mutex) == SCE_OK);
Require(scePthreadMutexLock(&mutex) == SCE_KERNEL_ERROR_EDEADLK);
RequireTimesOut([&](KernelUseconds usec) { return scePthreadMutexTimedlock(&mutex, usec); });
Require(scePthreadMutexDestroy(&mutex) == SCE_KERNEL_ERROR_EBUSY);
Require(scePthreadMutexUnlock(&mutex) == SCE_OK);
Require(scePthreadMutexDestroy(&mutex) == SCE_OK);
Require(scePthreadMutexattrInit(&attr) == SCE_OK);
Require(scePthreadMutexattrSettype(&attr, MUTEX_TYPE_ADAPTIVE) == SCE_OK);
PthreadMutex adaptive = nullptr;
Require(scePthreadMutexInit(&adaptive, &attr, nullptr) == SCE_OK);
Require(scePthreadMutexattrDestroy(&attr) == SCE_OK);
Require(scePthreadMutexLock(&adaptive) == SCE_OK);
Require(scePthreadMutexLock(&adaptive) == SCE_KERNEL_ERROR_EDEADLK);
RequireTimesOut([&](KernelUseconds usec) { return scePthreadMutexTimedlock(&adaptive, usec); });
Require(scePthreadMutexUnlock(&adaptive) == SCE_OK);
Require(scePthreadMutexDestroy(&adaptive) == SCE_OK);
PthreadMutex timedDefault = nullptr;
Require(scePthreadMutexInit(&timedDefault, nullptr, nullptr) == SCE_OK);
Require(scePthreadMutexLock(&timedDefault) == SCE_OK);
RequireTimesOut([&](KernelUseconds usec) { return scePthreadMutexTimedlock(&timedDefault, usec); });
Require(scePthreadMutexUnlock(&timedDefault) == SCE_OK);
Require(scePthreadMutexTimedlock(&timedDefault, 20000) == SCE_OK);
Require(scePthreadMutexUnlock(&timedDefault) == SCE_OK);
Require(scePthreadMutexDestroy(&timedDefault) == SCE_OK);
Require(scePthreadMutexattrInit(&attr) == SCE_OK);
Require(scePthreadMutexattrSettype(&attr, MUTEX_TYPE_NORMAL) == SCE_OK);
PthreadMutex normal = nullptr;
Require(scePthreadMutexInit(&normal, &attr, nullptr) == SCE_OK);
Require(scePthreadMutexattrDestroy(&attr) == SCE_OK);
Require(scePthreadMutexLock(&normal) == SCE_OK);
RequireTimesOut([&](KernelUseconds usec) { return scePthreadMutexTimedlock(&normal, usec); });
Require(scePthreadMutexUnlock(&normal) == SCE_OK);
Require(scePthreadMutexDestroy(&normal) == SCE_OK);
PthreadRwlock rwlock = nullptr;
Require(scePthreadRwlockInit(&rwlock, nullptr, nullptr) == SCE_OK);
Require(scePthreadRwlockWrlock(&rwlock) == SCE_OK);
Require(scePthreadRwlockTrywrlock(&rwlock) == SCE_KERNEL_ERROR_EBUSY);
Require(scePthreadRwlockWrlock(&rwlock) == SCE_KERNEL_ERROR_EDEADLK);
RequireTimesOut([&](KernelUseconds usec) { return scePthreadRwlockTimedrdlock(&rwlock, usec); });
RequireTimesOut([&](KernelUseconds usec) { return scePthreadRwlockTimedwrlock(&rwlock, usec); });
Require(scePthreadRwlockUnlock(&rwlock) == SCE_OK);
Require(scePthreadRwlockTimedwrlock(&rwlock, 20000) == SCE_OK);
Require(scePthreadRwlockUnlock(&rwlock) == SCE_OK);
Require(scePthreadRwlockDestroy(&rwlock) == SCE_OK);
PthreadMutex adaptiveStatic = reinterpret_cast<PthreadMutex>(std::uintptr_t{1});
Require(scePthreadMutexLock(&adaptiveStatic) == SCE_OK);
Require(adaptiveStatic != reinterpret_cast<PthreadMutex>(std::uintptr_t{1}));
Require(scePthreadMutexLock(&adaptiveStatic) == SCE_KERNEL_ERROR_EDEADLK);
Require(scePthreadMutexTrylock(&adaptiveStatic) == SCE_KERNEL_ERROR_EBUSY);
Require(scePthreadMutexUnlock(&adaptiveStatic) == SCE_OK);
Require(scePthreadMutexDestroy(&adaptiveStatic) == SCE_OK);
adaptiveStatic = reinterpret_cast<PthreadMutex>(std::uintptr_t{1});
Require(scePthreadMutexTrylock(&adaptiveStatic) == SCE_OK);
Require(scePthreadMutexTrylock(&adaptiveStatic) == SCE_KERNEL_ERROR_EBUSY);
Require(scePthreadMutexUnlock(&adaptiveStatic) == SCE_OK);
Require(scePthreadMutexDestroy(&adaptiveStatic) == SCE_OK);
adaptiveStatic = reinterpret_cast<PthreadMutex>(std::uintptr_t{1});
Require(scePthreadMutexDestroy(&adaptiveStatic) == SCE_OK);
Require(scePthreadMutexattrInit(&attr) == SCE_OK);
PthreadMutex defaulted = nullptr;
Require(scePthreadMutexInit(&defaulted, &attr, nullptr) == SCE_OK);
Require(scePthreadMutexattrDestroy(&attr) == SCE_OK);
Require(scePthreadMutexLock(&defaulted) == SCE_OK);
Require(scePthreadMutexLock(&defaulted) == SCE_KERNEL_ERROR_EDEADLK);
Require(scePthreadMutexUnlock(&defaulted) == SCE_OK);
Require(scePthreadMutexDestroy(&defaulted) == SCE_OK);
PthreadMutex unattributed = nullptr;
Require(scePthreadMutexInit(&unattributed, nullptr, nullptr) == SCE_OK);
Require(scePthreadMutexLock(&unattributed) == SCE_OK);
Require(scePthreadMutexLock(&unattributed) == SCE_KERNEL_ERROR_EDEADLK);
Require(scePthreadMutexUnlock(&unattributed) == SCE_OK);
Require(scePthreadMutexDestroy(&unattributed) == SCE_OK);
Require(pthread_mutexattr_init_nid_postfix(&attr) == 0);
Require(pthread_mutexattr_settype_nid_postfix(&attr, MUTEX_TYPE_ADAPTIVE) == 0);
PthreadMutex posixAdaptive = nullptr;
Require(pthread_mutex_init_nid_postfix(&posixAdaptive, &attr) == 0);
Require(pthread_mutexattr_destroy_nid_postfix(&attr) == 0);
Require(pthread_mutex_lock_nid_postfix(&posixAdaptive) == 0);
Require(pthread_mutex_lock_nid_postfix(&posixAdaptive) == POSIX_EDEADLK);
Require(pthread_mutex_unlock_nid_postfix(&posixAdaptive) == 0);
Require(pthread_mutex_destroy_nid_postfix(&posixAdaptive) == 0);
auto staticAdaptive = reinterpret_cast<PthreadMutex>(std::uintptr_t{1});
Require(pthread_mutex_lock_nid_postfix(&staticAdaptive) == 0);
Require(pthread_mutex_lock_nid_postfix(&staticAdaptive) == POSIX_EDEADLK);
Require(pthread_mutex_unlock_nid_postfix(&staticAdaptive) == 0);
Require(pthread_mutex_destroy_nid_postfix(&staticAdaptive) == 0);
KernelEventFlag ef = nullptr;
Require(sceKernelCreateEventFlag(nullptr, "test", EVF_ATTR_MULTI, 0, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelCreateEventFlag(&ef, nullptr, EVF_ATTR_MULTI, 0, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelCreateEventFlag(&ef, "test_zero_attr", 0, 0, nullptr) == SCE_OK);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "test_fifo", 0x01, 0, nullptr) == SCE_OK);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "test_single_fifo", EVF_ATTR_SINGLE | 0x01u, 0, nullptr) == SCE_OK);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "test", EVF_ATTR_SINGLE | EVF_ATTR_MULTI, 0, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelCreateEventFlag(&ef, "test", EVF_ATTR_MULTI | 0x1000u, 0, nullptr) == SCE_KERNEL_ERROR_EINVAL);
for (const std::uint32_t attr : {0x03u, 0x13u, 0x23u, 0x103u, 0x130u, 0x04u, 0x08u, 0x40u, 0x80u, 0x200u, 0x80000000u, 0xffffffffu}) {
Require(sceKernelCreateEventFlag(&ef, "test_bad_attr", attr, 0, nullptr) == SCE_KERNEL_ERROR_EINVAL);
}
for (const std::uint32_t attr : {0x02u, 0x11u, 0x12u, 0x21u, 0x22u}) {
Require(sceKernelCreateEventFlag(&ef, "test_attr", attr, 0, nullptr) == SCE_OK);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
}
Require(sceKernelDeleteEventFlag(nullptr) == SCE_KERNEL_ERROR_ESRCH);
Require(sceKernelSetEventFlag(nullptr, 1) == SCE_KERNEL_ERROR_ESRCH);
Require(sceKernelClearEventFlag(nullptr, 1) == SCE_KERNEL_ERROR_ESRCH);
Require(sceKernelPollEventFlag(nullptr, 1, EVF_WAITMODE_OR, nullptr) == SCE_KERNEL_ERROR_ESRCH);
Require(sceKernelWaitEventFlag(nullptr, 1, EVF_WAITMODE_OR, nullptr, nullptr) == SCE_KERNEL_ERROR_ESRCH);
Require(sceKernelCancelEventFlag(nullptr, 0, nullptr) == SCE_KERNEL_ERROR_ESRCH);
Require(sceKernelCreateEventFlag(&ef, "", EVF_ATTR_MULTI, 0, nullptr) == SCE_OK);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn", EVF_ATTR_SHARED | EVF_ATTR_MULTI, 0, nullptr) == SCE_OK);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
for (const std::uint32_t attr : {EVF_ATTR_MULTI, EVF_ATTR_SHARED | EVF_ATTR_MULTI}) {
Require(sceKernelCreateEventFlag(&ef, "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn", attr, 0, nullptr) == SCE_KERNEL_ERROR_ENAMETOOLONG);
}
KernelEventFlag opened = nullptr;
Require(sceKernelOpenEventFlag(nullptr, "test") == SCE_KERNEL_ERROR_ESRCH);
Require(sceKernelOpenEventFlag(&opened, nullptr) == SCE_KERNEL_ERROR_EFAULT && opened == nullptr);
Require(sceKernelOpenEventFlag(&opened, "") == SCE_KERNEL_ERROR_ESRCH && opened == nullptr);
Require(sceKernelOpenEventFlag(&opened, "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn") == SCE_KERNEL_ERROR_ENAMETOOLONG && opened == nullptr);
Require(sceKernelOpenEventFlag(&opened, "SceShellCoreUtilAppFocus") == SCE_KERNEL_ERROR_ESRCH && opened == nullptr);
Require(sceKernelCloseEventFlag(nullptr) == SCE_KERNEL_ERROR_ESRCH);
KernelEventFlag plain = nullptr;
Require(sceKernelCreateEventFlag(&plain, "test_open", EVF_ATTR_MULTI, 0x01, nullptr) == SCE_OK);
Require(sceKernelOpenEventFlag(&opened, "test_open") == SCE_KERNEL_ERROR_ESRCH && opened == nullptr);
Require(sceKernelCloseEventFlag(plain) == SCE_KERNEL_ERROR_EPERM);
Require(sceKernelSetEventFlag(plain, 0x02) == SCE_OK);
KernelEventFlag plainTwin = nullptr;
Require(sceKernelCreateEventFlag(&plainTwin, "test_open", EVF_ATTR_MULTI, 0, nullptr) == SCE_OK);
Require(sceKernelDeleteEventFlag(plainTwin) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "test_open", EVF_ATTR_SHARED | EVF_ATTR_MULTI, 0x05, nullptr) == SCE_OK);
KernelEventFlag sharedTwin = nullptr;
Require(sceKernelCreateEventFlag(&sharedTwin, "test_open", EVF_ATTR_SHARED | EVF_ATTR_MULTI, 0, nullptr) == SCE_KERNEL_ERROR_EPERM && sharedTwin == nullptr);
Require(sceKernelOpenEventFlag(&opened, "test_open") == SCE_OK && opened != nullptr && opened != ef && opened != plain);
Require(sceKernelOpenEventFlag(nullptr, "test_open") == SCE_KERNEL_ERROR_EFAULT);
KernelEventFlag openedTwice = nullptr;
Require(sceKernelOpenEventFlag(&openedTwice, "test_open") == SCE_OK && openedTwice != opened && openedTwice != ef);
Require(sceKernelSetEventFlag(opened, 0x08) == SCE_OK);
Require(sceKernelClearEventFlag(openedTwice, ~std::uint64_t{0x04}) == SCE_OK);
std::uint64_t openedPattern = 0;
Require(sceKernelPollEventFlag(ef, 0x08, EVF_WAITMODE_OR, &openedPattern) == SCE_OK && openedPattern == 0x09);
Require(sceKernelDeleteEventFlag(opened) == SCE_KERNEL_ERROR_EPERM);
Require(sceKernelCloseEventFlag(ef) == SCE_KERNEL_ERROR_EPERM);
Require(sceKernelCloseEventFlag(openedTwice) == SCE_OK);
Require(sceKernelCloseEventFlag(openedTwice) == SCE_KERNEL_ERROR_ESRCH);
Require(sceKernelSetEventFlag(openedTwice, 0x01) == SCE_KERNEL_ERROR_ESRCH);
std::atomic<int> orphanWaitRes{-1};
std::uint64_t orphanWaitPat = 0xdeadbeef;
std::thread orphanWaiter([&] {
orphanWaitRes = sceKernelWaitEventFlag(opened, 0x80, EVF_WAITMODE_OR, &orphanWaitPat, nullptr);
});
WaitForEventFlagWaiters(opened, 1);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
orphanWaiter.join();
Require(orphanWaitRes == SCE_KERNEL_ERROR_EACCES && orphanWaitPat == 0);
KernelEventFlag reopened = nullptr;
Require(sceKernelOpenEventFlag(&reopened, "test_open") == SCE_KERNEL_ERROR_ESRCH && reopened == nullptr);
std::uint64_t orphanPattern = 0xdeadbeef;
Require(sceKernelPollEventFlag(opened, 0, EVF_WAITMODE_OR, &orphanPattern) == SCE_KERNEL_ERROR_EINVAL && orphanPattern == 0xdeadbeef);
Require(sceKernelPollEventFlag(opened, 0x01, EVF_WAITMODE_OR, &orphanPattern) == SCE_KERNEL_ERROR_EACCES && orphanPattern == 0);
orphanPattern = 0xdeadbeef;
KernelUseconds orphanTimeout = 1000;
Require(sceKernelWaitEventFlag(opened, 0x01, EVF_WAITMODE_OR, &orphanPattern, &orphanTimeout) == SCE_KERNEL_ERROR_EACCES && orphanPattern == 0 && orphanTimeout == 1000);
Require(sceKernelSetEventFlag(opened, 0x02) == SCE_OK);
Require(sceKernelClearEventFlag(opened, 0) == SCE_OK);
int orphanCount = -1;
Require(sceKernelCancelEventFlag(opened, 0x04, &orphanCount) == SCE_OK && orphanCount == 0);
Require(sceKernelDeleteEventFlag(opened) == SCE_KERNEL_ERROR_EPERM);
Require(sceKernelCreateEventFlag(&ef, "test_open", EVF_ATTR_SHARED | EVF_ATTR_SINGLE, 0x01, nullptr) == SCE_OK);
Require(sceKernelOpenEventFlag(&reopened, "test_open") == SCE_OK && reopened != opened);
Require(sceKernelCloseEventFlag(opened) == SCE_OK);
Require(sceKernelCloseEventFlag(opened) == SCE_KERNEL_ERROR_ESRCH);
std::atomic<int> sharedSingleRes{-1};
std::uint64_t sharedSinglePat = 0;
std::thread sharedSingleWaiter([&] {
sharedSingleRes = sceKernelWaitEventFlag(ef, 0x80, EVF_WAITMODE_OR, &sharedSinglePat, nullptr);
});
WaitForEventFlagWaiters(ef, 1);
openedPattern = 0xdeadbeef;
Require(sceKernelPollEventFlag(reopened, 0x01, EVF_WAITMODE_OR, &openedPattern) == SCE_KERNEL_ERROR_EPERM && openedPattern == 0);
Require(sceKernelSetEventFlag(reopened, 0x80) == SCE_OK);
sharedSingleWaiter.join();
Require(sharedSingleRes == SCE_OK && sharedSinglePat == 0x81);
Require(sceKernelCloseEventFlag(reopened) == SCE_OK);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelDeleteEventFlag(plain) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "flag", EVF_ATTR_MULTI, 0x15, nullptr) == SCE_OK);
std::uint64_t pattern = 0;
Require(sceKernelPollEventFlag(ef, 0, EVF_WAITMODE_OR, &pattern) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelPollEventFlag(ef, 1, 0, &pattern) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelPollEventFlag(ef, 1, EVF_WAITMODE_AND | EVF_WAITMODE_OR, &pattern) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelPollEventFlag(ef, 1, EVF_WAITMODE_AND | EVF_WAITMODE_CLEAR_ALL | EVF_WAITMODE_CLEAR_PAT, &pattern) == SCE_KERNEL_ERROR_EINVAL);
for (const std::uint32_t mode : {0x04u, 0x40u, 0x100u, 0x80000000u, 0x103u}) {
pattern = 0xdeadbeef;
Require(sceKernelPollEventFlag(ef, 1, mode, &pattern) == SCE_KERNEL_ERROR_EINVAL && pattern == 0xdeadbeef);
}
for (const std::uint32_t mode : {EVF_WAITMODE_OR | 0x04u, EVF_WAITMODE_OR | 0x08u, EVF_WAITMODE_AND | 0x40u, EVF_WAITMODE_AND | 0x100u, EVF_WAITMODE_OR | 0x80000000u,
EVF_WAITMODE_OR | 0xffffff00u}) {
Require(sceKernelPollEventFlag(ef, 0x10, mode, &pattern) == SCE_OK && pattern == 0x15);
}
Require(sceKernelPollEventFlag(ef, 0x10, EVF_WAITMODE_OR | EVF_WAITMODE_CLEAR_PAT | 0x100u, &pattern) == SCE_OK && pattern == 0x15);
Require(sceKernelSetEventFlag(ef, 0x10) == SCE_OK);
Require(sceKernelPollEventFlag(ef, 0x20, EVF_WAITMODE_OR, &pattern) == SCE_KERNEL_ERROR_EBUSY);
Require(pattern == 0x15);
Require(sceKernelPollEventFlag(ef, 0x11, EVF_WAITMODE_AND, &pattern) == SCE_OK);
Require(pattern == 0x15);
Require(sceKernelClearEventFlag(ef, ~std::uint64_t{0x01}) == SCE_OK);
Require(sceKernelPollEventFlag(ef, 0x01, EVF_WAITMODE_OR, &pattern) == SCE_KERNEL_ERROR_EBUSY);
Require(pattern == 0x14);
Require(sceKernelSetEventFlag(ef, 0x02) == SCE_OK);
Require(sceKernelPollEventFlag(ef, 0x06, EVF_WAITMODE_OR | EVF_WAITMODE_CLEAR_PAT, &pattern) == SCE_OK);
Require(pattern == 0x16);
Require(sceKernelPollEventFlag(ef, 0x02, EVF_WAITMODE_OR, &pattern) == SCE_KERNEL_ERROR_EBUSY);
Require(pattern == 0x10);
Require(sceKernelPollEventFlag(ef, 0x10, EVF_WAITMODE_OR | EVF_WAITMODE_CLEAR_ALL, &pattern) == SCE_OK);
Require(pattern == 0x10);
Require(sceKernelPollEventFlag(ef, 0x10, EVF_WAITMODE_OR, &pattern) == SCE_KERNEL_ERROR_EBUSY);
Require(pattern == 0);
KernelUseconds efTimeout = 1000;
Require(sceKernelWaitEventFlag(ef, 0x01, EVF_WAITMODE_OR, &pattern, &efTimeout) == SCE_KERNEL_ERROR_ETIMEDOUT);
Require(pattern == 0);
int waitThreads = -1;
Require(sceKernelCancelEventFlag(ef, 0x42, &waitThreads) == SCE_OK);
Require(waitThreads == 0);
Require(sceKernelPollEventFlag(ef, 0x42, EVF_WAITMODE_AND, &pattern) == SCE_OK);
Require(pattern == 0x42);
Require(sceKernelClearEventFlag(ef, 0) == SCE_OK);
Require(sceKernelPollEventFlag(ef, 0x42, EVF_WAITMODE_AND, &pattern) == SCE_KERNEL_ERROR_EBUSY);
Require(pattern == 0);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "single", EVF_ATTR_SINGLE | 0x02u, 0x04, nullptr) == SCE_OK);
std::atomic<int> singleRes{-1};
std::uint64_t singlePat = 0xdeadbeef;
std::thread singleWaiter([&] {
singleRes = sceKernelWaitEventFlag(ef, 0x01, EVF_WAITMODE_AND, &singlePat, nullptr);
});
WaitForEventFlagWaiters(ef, 1);
pattern = 0xdeadbeef;
Require(sceKernelPollEventFlag(ef, 0x04, EVF_WAITMODE_AND, &pattern) == SCE_KERNEL_ERROR_EPERM && pattern == 0);
pattern = 0xdeadbeef;
Require(sceKernelWaitEventFlag(ef, 0x04, EVF_WAITMODE_AND, &pattern, nullptr) == SCE_KERNEL_ERROR_EPERM && pattern == 0);
waitThreads = -1;
Require(sceKernelCancelEventFlag(ef, 0x08, &waitThreads) == SCE_OK && waitThreads == 1);
singleWaiter.join();
Require(singleRes == SCE_KERNEL_ERROR_ECANCELED && singlePat == 0);
Require(sceKernelPollEventFlag(ef, 0x08, EVF_WAITMODE_AND, &pattern) == SCE_OK && pattern == 0x08);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "multi_cancel", EVF_ATTR_MULTI, 0x01, nullptr) == SCE_OK);
std::atomic<int> cancelRes1{-1};
std::atomic<int> cancelRes2{-1};
std::uint64_t cancelPat1 = 0xdeadbeef;
std::uint64_t cancelPat2 = 0xdeadbeef;
std::thread cancelT1([&] {
cancelRes1 = sceKernelWaitEventFlag(ef, 0x02, EVF_WAITMODE_AND, &cancelPat1, nullptr);
});
std::thread cancelT2([&] {
cancelRes2 = sceKernelWaitEventFlag(ef, 0x04, EVF_WAITMODE_OR, &cancelPat2, nullptr);
});
WaitForEventFlagWaiters(ef, 2);
waitThreads = -1;
Require(sceKernelCancelEventFlag(ef, 0x05, &waitThreads) == SCE_OK && waitThreads == 2);
cancelT1.join();
cancelT2.join();
Require(cancelRes1 == SCE_KERNEL_ERROR_ECANCELED && cancelPat1 == 0);
Require(cancelRes2 == SCE_KERNEL_ERROR_ECANCELED && cancelPat2 == 0);
Require(sceKernelPollEventFlag(ef, 0x05, EVF_WAITMODE_AND, &pattern) == SCE_OK && pattern == 0x05);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "imm_sat", EVF_ATTR_MULTI, 0x05, nullptr) == SCE_OK);
pattern = 0;
Require(sceKernelWaitEventFlag(ef, 0x01, EVF_WAITMODE_AND | EVF_WAITMODE_CLEAR_PAT, &pattern, nullptr) == SCE_OK);
Require(pattern == 0x05);
Require(sceKernelPollEventFlag(ef, 0x01, EVF_WAITMODE_AND, &pattern) == SCE_KERNEL_ERROR_EBUSY);
Require(sceKernelPollEventFlag(ef, 0x04, EVF_WAITMODE_AND, &pattern) == SCE_OK);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "multi_order_nonclear_first", EVF_ATTR_MULTI, 0, nullptr) == SCE_OK);
std::atomic<int> a1Res{-1};
std::atomic<int> a2Res{-1};
std::uint64_t a1Pat = 0;
std::uint64_t a2Pat = 0;
std::thread a1([&] {
a1Res = sceKernelWaitEventFlag(ef, 0x01, EVF_WAITMODE_AND, &a1Pat, nullptr);
});
WaitForEventFlagWaiters(ef, 1);
std::thread a2([&] {
a2Res = sceKernelWaitEventFlag(ef, 0x01, EVF_WAITMODE_AND | EVF_WAITMODE_CLEAR_PAT, &a2Pat, nullptr);
});
WaitForEventFlagWaiters(ef, 2);
Require(sceKernelSetEventFlag(ef, 0x01) == SCE_OK);
a1.join();
a2.join();
Require(a1Res == SCE_OK);
Require(a2Res == SCE_OK);
Require(a1Pat == 0x01);
Require(a2Pat == 0x01);
Require(sceKernelPollEventFlag(ef, 0x01, EVF_WAITMODE_AND, &pattern) == SCE_KERNEL_ERROR_EBUSY);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "multi_order_clear_first", EVF_ATTR_MULTI, 0, nullptr) == SCE_OK);
std::atomic<int> b1Res{-1};
std::atomic<int> b2Res{-1};
std::uint64_t b1Pat = 0;
std::uint64_t b2Pat = 0;
std::thread b1([&] {
b1Res = sceKernelWaitEventFlag(ef, 0x01, EVF_WAITMODE_AND | EVF_WAITMODE_CLEAR_PAT, &b1Pat, nullptr);
});
WaitForEventFlagWaiters(ef, 1);
std::thread b2([&] {
b2Res = sceKernelWaitEventFlag(ef, 0x01, EVF_WAITMODE_AND, &b2Pat, nullptr);
});
WaitForEventFlagWaiters(ef, 2);
Require(sceKernelSetEventFlag(ef, 0x01) == SCE_OK);
b1.join();
Require(b1Res == SCE_OK);
Require(b1Pat == 0x01);
Require(APS5_Test_GetEventFlagWaiterCount(ef) == 1);
Require(b2Res == -1);
Require(sceKernelSetEventFlag(ef, 0x01) == SCE_OK);
b2.join();
Require(b2Res == SCE_OK);
Require(b2Pat == 0x01);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
Require(sceKernelCreateEventFlag(&ef, "del_waiters", EVF_ATTR_MULTI, 0, nullptr) == SCE_OK);
std::atomic<int> delRes1{-1};
std::atomic<int> delRes2{-1};
std::uint64_t delPat = 0xdeadbeef;
std::thread delT1([&] {
delRes1 = sceKernelWaitEventFlag(ef, 0x01, EVF_WAITMODE_AND, &delPat, nullptr);
});
std::thread delT2([&] {
delRes2 = sceKernelWaitEventFlag(ef, 0x02, EVF_WAITMODE_AND, nullptr, nullptr);
});
WaitForEventFlagWaiters(ef, 2);
Require(sceKernelDeleteEventFlag(ef) == SCE_OK);
delT1.join();
delT2.join();
Require(delRes1 == SCE_KERNEL_ERROR_EACCES && delPat == 0);
Require(delRes2 == SCE_KERNEL_ERROR_EACCES);
for (int iter = 0; iter < 50; ++iter) {
KernelEventFlag raceEf = nullptr;
Require(sceKernelCreateEventFlag(&raceEf, "set_del_race", EVF_ATTR_MULTI, 0, nullptr) == SCE_OK);
std::atomic<int> setResult{-1};
std::thread concurrentSet([&] {
setResult = sceKernelSetEventFlag(raceEf, 0x01);
});
Require(sceKernelDeleteEventFlag(raceEf) == SCE_OK);
concurrentSet.join();
Require(setResult == SCE_OK || setResult == SCE_KERNEL_ERROR_ESRCH);
}
for (int iter = 0; iter < 50; ++iter) {
KernelEventFlag pollEf = nullptr;
Require(sceKernelCreateEventFlag(&pollEf, "poll_del_race", EVF_ATTR_MULTI, 0x01, nullptr) == SCE_OK);
std::atomic<int> pollResult{-1};
std::thread concurrentPoll([&] {
std::uint64_t observed = 0;
pollResult = sceKernelPollEventFlag(pollEf, 0x01, EVF_WAITMODE_AND, &observed);
});
Require(sceKernelDeleteEventFlag(pollEf) == SCE_OK);
concurrentPoll.join();
Require(pollResult == SCE_OK || pollResult == SCE_KERNEL_ERROR_ESRCH);
}
}