97 lines
4.4 KiB
C++
97 lines
4.4 KiB
C++
#include "SceTypes.hpp"
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <thread>
|
|
|
|
extern "C" {
|
|
int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name);
|
|
int APS5_VABI scePthreadJoin(Pthread thread, void** retval);
|
|
int APS5_VABI scePthreadCancel(Pthread thread);
|
|
void APS5_VABI scePthreadExit(void* retval);
|
|
Pthread APS5_VABI scePthreadSelf();
|
|
int APS5_VABI scePthreadGetthreadid();
|
|
int APS5_VABI scePthreadGetname(Pthread thread, char* name);
|
|
int APS5_VABI scePthreadKeyCreate(PthreadKey* key, pthread_key_destructor_func_t destructor);
|
|
int APS5_VABI scePthreadKeyDelete(PthreadKey key);
|
|
void* APS5_VABI scePthreadGetspecific(PthreadKey key);
|
|
int APS5_VABI scePthreadSetspecific(PthreadKey key, void* value);
|
|
int APS5_VABI sceKernelUsleep_nid_postfix(KernelUseconds microseconds);
|
|
}
|
|
|
|
struct Context {
|
|
PthreadKey key = -1;
|
|
Pthread expected = nullptr;
|
|
int expectedId = 0;
|
|
unsigned calls = 0;
|
|
unsigned failures = 0;
|
|
bool exitExplicitly = false;
|
|
bool pendingCancel = false;
|
|
|
|
void Check(bool passed, const char* message) {
|
|
if (passed) return;
|
|
std::fprintf(stderr, "%s: %s\n", exitExplicitly ? "explicit exit" : "return", message);
|
|
++failures;
|
|
}
|
|
};
|
|
|
|
static void APS5_VABI DestroyValue(void* value) {
|
|
auto& context = *static_cast<Context*>(value);
|
|
++context.calls;
|
|
context.Check(scePthreadGetspecific(context.key) == nullptr, "key value was not cleared before its destructor");
|
|
if (context.expected) {
|
|
const auto self = scePthreadSelf();
|
|
context.Check(self == context.expected, "key destructor lost the exiting thread identity");
|
|
context.Check(scePthreadGetthreadid() == context.expectedId, "key destructor lost the exiting thread ID");
|
|
char name[32]{};
|
|
context.Check(scePthreadGetname(self, name) == 0 && std::strcmp(name, "KeyDestructor") == 0,
|
|
"key destructor lost the exiting thread name");
|
|
context.Check(sceKernelUsleep_nid_postfix(1000) == 0, "key destructor could not wait");
|
|
}
|
|
context.Check(scePthreadSetspecific(context.key, value) == 0, "key destructor could not restore its value");
|
|
}
|
|
|
|
static void* APS5_VABI Worker(void* value) {
|
|
auto& context = *static_cast<Context*>(value);
|
|
context.expected = scePthreadSelf();
|
|
context.expectedId = scePthreadGetthreadid();
|
|
context.Check(scePthreadSetspecific(context.key, value) == 0, "worker could not bind its key value");
|
|
if (context.pendingCancel) context.Check(scePthreadCancel(context.expected) == 0, "worker could not request cancellation");
|
|
if (context.exitExplicitly) scePthreadExit(value);
|
|
return value;
|
|
}
|
|
|
|
static bool RunGuest(bool exitExplicitly, bool pendingCancel = false) {
|
|
Context context;
|
|
context.exitExplicitly = exitExplicitly;
|
|
context.pendingCancel = pendingCancel;
|
|
if (scePthreadKeyCreate(&context.key, reinterpret_cast<pthread_key_destructor_func_t>(DestroyValue)) != 0) return false;
|
|
Pthread thread = nullptr;
|
|
if (scePthreadCreate(&thread, nullptr, Worker, &context, "KeyDestructor") != 0) return false;
|
|
void* result = nullptr;
|
|
context.Check(scePthreadJoin(thread, &result) == 0 && result == &context, "thread did not join with its return value");
|
|
context.Check(context.calls == 4, "key destructor did not stop after four iterations");
|
|
context.Check(scePthreadKeyDelete(context.key) == 0, "key deletion failed");
|
|
return context.failures == 0;
|
|
}
|
|
|
|
static bool RunHost() {
|
|
Context context;
|
|
if (scePthreadKeyCreate(&context.key, reinterpret_cast<pthread_key_destructor_func_t>(DestroyValue)) != 0) return false;
|
|
std::thread worker([&] {
|
|
context.Check(scePthreadSetspecific(context.key, &context) == 0, "host worker could not bind its key value");
|
|
});
|
|
worker.join();
|
|
context.Check(context.calls == 4, "host thread did not run four key destructor iterations");
|
|
context.Check(scePthreadKeyDelete(context.key) == 0, "host key deletion failed");
|
|
return context.failures == 0;
|
|
}
|
|
|
|
int main() {
|
|
const bool returned = RunGuest(false);
|
|
const bool exited = RunGuest(true);
|
|
const bool canceled = RunGuest(false, true);
|
|
const bool host = RunHost();
|
|
if (!returned || !exited || !canceled || !host) return 1;
|
|
std::puts("PASS: pthread key destructors retain thread identity on return and explicit exit; pending cancellation, four iterations and host cleanup preserved");
|
|
}
|