Files
AnyPS5/core/libs/tests/GuestPosixSpecific.cpp
xXPBSXxandClaude Sonnet 5.5 d6ec92312c test(libkernel): cover pthread_setspecific and pthread_getspecific
pthread_setspecific returns an errno value, unlike the Sce function it wraps,
and neither POSIX export had a test. Check the FreeBSD 11.4 results of
_pthread_setspecific and _pthread_getspecific (lib/libthr/thread/thr_spec.c
lines 165-194 and 196-222): a key outside the valid range or a deleted key
makes setspecific return EINVAL (22), getspecific returns NULL for such a key
and for a key never set, a stored value is read back, NULL can be stored, a
new key with a reused number starts empty, and another thread has its own
value.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-27 08:53:33 +01:00

78 lines
3.5 KiB
C++

#include "prx/libkernel/include/Api.hpp"
#include "SceTypes.hpp"
#include <atomic>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <thread>
namespace {
constexpr int Einval = 22;
void Require(bool value, const char* message) {
if (!value) {
std::fprintf(stderr, "%s\n", message);
std::abort();
}
}
void WaitFor(const std::atomic<bool>& flag, const char* message) {
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
while (!flag && std::chrono::steady_clock::now() < deadline) std::this_thread::sleep_for(std::chrono::milliseconds(1));
Require(flag, message);
}
}
int main() {
int first = 0;
int second = 0;
for (const PthreadKey invalid : {static_cast<PthreadKey>(0), static_cast<PthreadKey>(0xFFFFFFFFu)}) {
Require(pthread_setspecific_nid_postfix(invalid, &first) == Einval, "setspecific of an out-of-range key returns EINVAL");
Require(pthread_getspecific_nid_postfix(invalid) == nullptr, "getspecific of an out-of-range key returns NULL");
}
PthreadKey key = 0;
Require(pthread_key_create_nid_postfix(&key, nullptr) == 0 && key != 0, "key create");
Require(pthread_getspecific_nid_postfix(key) == nullptr, "getspecific of a key never set returns NULL");
Require(pthread_setspecific_nid_postfix(key, &first) == 0, "setspecific returns 0");
Require(pthread_getspecific_nid_postfix(key) == &first, "getspecific returns the stored value");
Require(pthread_setspecific_nid_postfix(key, nullptr) == 0, "setspecific of NULL returns 0");
Require(pthread_getspecific_nid_postfix(key) == nullptr, "getspecific after storing NULL returns NULL");
Require(pthread_setspecific_nid_postfix(key, &first) == 0, "setspecific again returns 0");
std::atomic<bool> stored{false};
std::atomic<bool> release{false};
std::atomic<bool> finished{false};
std::atomic<void*> seenBefore{&first};
std::atomic<void*> seenAfter{nullptr};
std::thread other([&] {
seenBefore = pthread_getspecific_nid_postfix(key);
Require(pthread_setspecific_nid_postfix(key, &second) == 0, "helper setspecific returns 0");
seenAfter = pthread_getspecific_nid_postfix(key);
stored = true;
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
while (!release && std::chrono::steady_clock::now() < deadline) std::this_thread::sleep_for(std::chrono::milliseconds(1));
finished = true;
});
WaitFor(stored, "helper never stored its value");
Require(seenBefore.load() == nullptr, "another thread starts with NULL for a key this thread has set");
Require(seenAfter.load() == &second, "another thread reads back its own value");
Require(pthread_getspecific_nid_postfix(key) == &first, "this thread still reads its own value");
release = true;
WaitFor(finished, "helper never finished");
other.join();
Require(pthread_key_delete_nid_postfix(key) == 0, "key delete returns 0");
Require(pthread_getspecific_nid_postfix(key) == nullptr, "getspecific of a deleted key returns NULL");
Require(pthread_setspecific_nid_postfix(key, &first) == Einval, "setspecific of a deleted key returns EINVAL");
PthreadKey reused = 0;
Require(pthread_key_create_nid_postfix(&reused, nullptr) == 0 && reused == key, "a new key reuses the freed number");
Require(pthread_getspecific_nid_postfix(reused) == nullptr, "the value stored under the old key is gone");
Require(pthread_key_delete_nid_postfix(reused) == 0, "second key delete returns 0");
return 0;
}