mirror of
https://github.com/Awakenchan/LanternEngine.git
synced 2026-11-10 01:11:45 -10:00
197 lines
7.7 KiB
C
197 lines
7.7 KiB
C
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
#define _GNU_SOURCE
|
|
#include "runtime.h"
|
|
#include "sys/sys.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
typedef void *Handle;
|
|
typedef void *(SYS_ABI *Entry)(void *);
|
|
|
|
static int failures;
|
|
#define EXPECT(condition) do { if (!(condition)) { printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #condition); ++failures; } } while (0)
|
|
|
|
static void *fn(const char *name) {
|
|
const SysLibrary *libraries[] = {&sys_thread, &sys_posix_thread};
|
|
for (size_t l = 0; l < 2; ++l)
|
|
for (size_t i = 0; i < libraries[l]->count; ++i)
|
|
if (!strcmp(libraries[l]->exports[i].name, name)) return libraries[l]->exports[i].function;
|
|
printf("FAIL missing %s\n", name);
|
|
++failures;
|
|
return NULL;
|
|
}
|
|
|
|
static void *(SYS_ABI *self)(void);
|
|
static int32_t *(SYS_ABI *error_location)(void);
|
|
static int32_t (SYS_ABI *attr_init)(Handle *);
|
|
static int32_t (SYS_ABI *attr_destroy)(Handle *);
|
|
static int32_t (SYS_ABI *attr_get)(Handle, Handle *);
|
|
static int32_t (SYS_ABI *attr_set_stack)(Handle *, uint64_t);
|
|
static int32_t (SYS_ABI *attr_get_stack)(Handle *, uint64_t *);
|
|
static int32_t (SYS_ABI *attr_set_detach)(Handle *, int);
|
|
static int32_t (SYS_ABI *attr_get_detach)(Handle *, int *);
|
|
static int32_t (SYS_ABI *thread_create)(Handle *, Handle *, Entry, void *, const char *);
|
|
static int32_t (SYS_ABI *thread_join)(Handle, void **);
|
|
static int32_t (SYS_ABI *thread_detach)(Handle);
|
|
static void (SYS_ABI *thread_exit)(void *);
|
|
static int32_t (SYS_ABI *get_prio)(Handle, int *);
|
|
static int32_t (SYS_ABI *set_prio)(Handle, int);
|
|
static int32_t (SYS_ABI *once)(int32_t *, void (SYS_ABI *)(void));
|
|
static int32_t (SYS_ABI *key_create)(uint32_t *, void (SYS_ABI *)(void *));
|
|
static int32_t (SYS_ABI *key_delete)(uint32_t);
|
|
static int32_t (SYS_ABI *key_set)(uint32_t, void *);
|
|
static void *(SYS_ABI *key_get)(uint32_t);
|
|
static int32_t (SYS_ABI *posix_create)(Handle *, Handle *, Entry, void *);
|
|
static int32_t (SYS_ABI *posix_join)(Handle, void **);
|
|
|
|
static void nap(unsigned ms) {
|
|
struct timespec t = {0, (long)ms * 1000000};
|
|
nanosleep(&t, NULL);
|
|
}
|
|
|
|
static uint32_t key;
|
|
static int destroyed;
|
|
static uintptr_t destroyed_value;
|
|
|
|
static void SYS_ABI destructor(void *value) {
|
|
__atomic_fetch_add(&destroyed, 1, __ATOMIC_SEQ_CST);
|
|
__atomic_store_n(&destroyed_value, (uintptr_t)value, __ATOMIC_SEQ_CST);
|
|
}
|
|
|
|
typedef struct { Handle handle; int same, deadlock, own_errno; } Probe;
|
|
|
|
static void SYS_ABI leave(void *value) { thread_exit(value); }
|
|
|
|
static void *SYS_ABI child(void *p) {
|
|
Probe *probe = p;
|
|
probe->same = self() == probe->handle;
|
|
probe->deadlock = thread_join(self(), NULL);
|
|
*error_location() = 99;
|
|
probe->own_errno = *error_location() == 99;
|
|
key_set(key, (void *)(uintptr_t)42);
|
|
leave((void *)(uintptr_t)0x5150);
|
|
return NULL;
|
|
}
|
|
|
|
static void *SYS_ABI returns(void *value) {
|
|
nap(20);
|
|
return value;
|
|
}
|
|
|
|
static int once_calls;
|
|
static int32_t once_word;
|
|
static void SYS_ABI once_routine(void) {
|
|
nap(10);
|
|
__atomic_fetch_add(&once_calls, 1, __ATOMIC_SEQ_CST);
|
|
}
|
|
static void *SYS_ABI once_caller(void *unused) {
|
|
(void)unused;
|
|
return (void *)(intptr_t)once(&once_word, once_routine);
|
|
}
|
|
|
|
static void attributes(void) {
|
|
Handle a = NULL, bogus = (Handle)(uintptr_t)0x1230;
|
|
uint64_t stack = 0;
|
|
int detached = -1;
|
|
EXPECT((uint32_t)attr_init(NULL) == 0x80020016 && attr_init(&a) == 0);
|
|
EXPECT(attr_get_stack(&a, &stack) == 0 && stack == 1024 * 1024);
|
|
EXPECT((uint32_t)attr_set_stack(&a, 16383) == 0x80020016 && attr_set_stack(&a, 16384) == 0);
|
|
EXPECT((uint32_t)attr_set_detach(&a, 2) == 0x80020016 && attr_set_detach(&a, 1) == 0);
|
|
EXPECT(attr_get_detach(&a, &detached) == 0 && detached == 1);
|
|
EXPECT((uint32_t)attr_set_stack(&bogus, 65536) == 0x80020016);
|
|
EXPECT((uint32_t)attr_get(bogus, &a) == 0x80020003);
|
|
EXPECT(attr_get(self(), &a) == 0 && attr_get_detach(&a, &detached) == 0 && detached == 0);
|
|
Handle copy = a;
|
|
EXPECT(attr_destroy(&a) == 0 && a == NULL && (uint32_t)attr_destroy(©) == 0x80020016);
|
|
}
|
|
|
|
static void threads(void) {
|
|
Handle main_thread = self();
|
|
EXPECT(main_thread != NULL && self() == main_thread);
|
|
EXPECT(key_create(&key, destructor) == 0 && key_set(key, (void *)(uintptr_t)7) == 0);
|
|
|
|
Probe probe = {0};
|
|
Handle attr = NULL;
|
|
attr_init(&attr);
|
|
EXPECT(thread_create(&probe.handle, &attr, child, &probe, "test-child") == 0);
|
|
void *result = NULL;
|
|
EXPECT(thread_join(probe.handle, &result) == 0 && result == (void *)(uintptr_t)0x5150);
|
|
EXPECT(probe.same && (uint32_t)probe.deadlock == 0x8002000b && probe.own_errno);
|
|
EXPECT(*error_location() != 99);
|
|
EXPECT(destroyed == 1 && destroyed_value == 42 && key_get(key) == (void *)(uintptr_t)7);
|
|
EXPECT((uint32_t)thread_join(probe.handle, NULL) == 0x80020016);
|
|
EXPECT((uint32_t)thread_join(main_thread, NULL) == 0x80020003);
|
|
EXPECT((uint32_t)thread_join((Handle)(uintptr_t)0x4560, NULL) == 0x80020003);
|
|
|
|
int prio = 0;
|
|
EXPECT(set_prio(probe.handle, 400) == 0 && get_prio(probe.handle, &prio) == 0 && prio == 400);
|
|
|
|
Handle running = NULL;
|
|
EXPECT(thread_create(&running, &attr, returns, NULL, "detached-later") == 0);
|
|
EXPECT(thread_detach(running) == 0 && (uint32_t)thread_detach(running) == 0x80020016);
|
|
EXPECT((uint32_t)thread_join(running, NULL) == 0x80020016);
|
|
int detached = -1;
|
|
EXPECT(attr_get(running, &attr) == 0 && attr_get_detach(&attr, &detached) == 0 && detached == 1);
|
|
attr_destroy(&attr);
|
|
|
|
Handle posix_thread = NULL;
|
|
EXPECT(posix_create(&posix_thread, NULL, returns, (void *)(uintptr_t)9) == 0);
|
|
EXPECT(posix_join(posix_thread, &result) == 0 && result == (void *)(uintptr_t)9);
|
|
EXPECT(posix_join(posix_thread, NULL) == 22 && posix_create(&posix_thread, NULL, NULL, NULL) == 22);
|
|
|
|
EXPECT(key_delete(key) == 0 && (uint32_t)key_delete(key) == 0x80020016 && (uint32_t)key_set(key, NULL) == 0x80020016);
|
|
uint32_t k = 0, made = 0;
|
|
while (key_create(&k, NULL) == 0) ++made;
|
|
EXPECT(made == 255);
|
|
EXPECT((uint32_t)key_create(&k, NULL) == 0x80020023);
|
|
for (uint32_t i = 1; i < 256; ++i) key_delete(i);
|
|
}
|
|
|
|
static void onces(void) {
|
|
Handle t[8];
|
|
void *r[8];
|
|
EXPECT((uint32_t)once(NULL, once_routine) == 0x80020016);
|
|
for (int i = 0; i < 8; ++i) EXPECT(thread_create(&t[i], NULL, once_caller, NULL, "once") == 0);
|
|
for (int i = 0; i < 8; ++i) EXPECT(thread_join(t[i], &r[i]) == 0 && r[i] == NULL);
|
|
EXPECT(once_calls == 1 && once_word == 1);
|
|
}
|
|
|
|
int main(void) {
|
|
self = fn("scePthreadSelf");
|
|
error_location = fn("__error");
|
|
attr_init = fn("scePthreadAttrInit");
|
|
attr_destroy = fn("scePthreadAttrDestroy");
|
|
attr_get = fn("scePthreadAttrGet");
|
|
attr_set_stack = fn("scePthreadAttrSetstacksize");
|
|
attr_get_stack = fn("scePthreadAttrGetstacksize");
|
|
attr_set_detach = fn("scePthreadAttrSetdetachstate");
|
|
attr_get_detach = fn("scePthreadAttrGetdetachstate");
|
|
thread_create = fn("scePthreadCreate");
|
|
thread_join = fn("scePthreadJoin");
|
|
thread_detach = fn("scePthreadDetach");
|
|
thread_exit = fn("scePthreadExit");
|
|
get_prio = fn("scePthreadGetprio");
|
|
set_prio = fn("scePthreadSetprio");
|
|
once = fn("scePthreadOnce");
|
|
key_create = fn("scePthreadKeyCreate");
|
|
key_delete = fn("scePthreadKeyDelete");
|
|
key_set = fn("scePthreadSetspecific");
|
|
key_get = fn("scePthreadGetspecific");
|
|
posix_create = fn("pthread_create");
|
|
posix_join = fn("pthread_join");
|
|
if (failures) return 1;
|
|
EXPECT(sys_resolve("6UgtwV+0zb4#p#J") == (uintptr_t)thread_create);
|
|
|
|
attributes();
|
|
threads();
|
|
onces();
|
|
|
|
uint64_t made = 0, finished = 0, found = 0, steps = 0;
|
|
runtime_thread_census(&made, &finished, &found, &steps);
|
|
EXPECT(made == 11 && found > 0 && steps <= found * 4);
|
|
|
|
printf("%s\n", failures ? "sys thread: FAILED" : "sys thread: ok");
|
|
return failures != 0;
|
|
}
|