161 lines
5.0 KiB
C
161 lines
5.0 KiB
C
// posix_thread.c - pthread_create, pthread_join, pthread_exit, pthread_mutex_*
|
|
#include <stddef.h>
|
|
#include "posix.h"
|
|
|
|
extern uint32_t SysGetCurrentPid(void);
|
|
extern uint32_t SysGetCurrentTid(void);
|
|
extern uint32_t SysCreateThread(uint64_t entry, uint64_t arg,
|
|
uint32_t priority, uint32_t* tidOut);
|
|
extern uint32_t SysWaitThread(uint32_t tid);
|
|
extern void SysExitThread(int code);
|
|
extern void* SysAlloc(size_t size);
|
|
extern void SysFree(void* ptr, size_t size);
|
|
extern uint32_t SysMutexCreate(uint32_t* handleOut);
|
|
extern uint32_t SysMutexAcquire(uint32_t handle);
|
|
extern uint32_t SysMutexTryAcquire(uint32_t handle);
|
|
extern uint32_t SysMutexRelease(uint32_t handle);
|
|
extern void SysCloseHandle(uint32_t handle);
|
|
|
|
// Thread return-value table (PID-keyed like the fd table)
|
|
#define PTHREAD_TABLE_PIDS 32
|
|
#define PTHREAD_TABLE_SIZE 32
|
|
|
|
typedef struct {
|
|
uint32_t Tid;
|
|
void* RetVal;
|
|
bool Used;
|
|
} PTHREAD_SLOT;
|
|
|
|
static PTHREAD_SLOT gPthreadTable[PTHREAD_TABLE_PIDS][PTHREAD_TABLE_SIZE];
|
|
|
|
static PTHREAD_SLOT* PthreadSlotAlloc(uint32_t pid, uint32_t tid) {
|
|
PTHREAD_SLOT* table = gPthreadTable[pid % PTHREAD_TABLE_PIDS];
|
|
for (int i = 0; i < PTHREAD_TABLE_SIZE; i++) {
|
|
if (!table[i].Used) {
|
|
table[i] = (PTHREAD_SLOT){ tid, (void*)0, true };
|
|
return &table[i];
|
|
}
|
|
}
|
|
return (PTHREAD_SLOT*)0;
|
|
}
|
|
|
|
static PTHREAD_SLOT* PthreadSlotFind(uint32_t pid, uint32_t tid) {
|
|
PTHREAD_SLOT* table = gPthreadTable[pid % PTHREAD_TABLE_PIDS];
|
|
for (int i = 0; i < PTHREAD_TABLE_SIZE; i++)
|
|
if (table[i].Used && table[i].Tid == tid)
|
|
return &table[i];
|
|
return (PTHREAD_SLOT*)0;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Trampoline - this is what actually runs as the new thread
|
|
// ---------------------------------------------------------------------------
|
|
typedef struct {
|
|
void* (*Fn)(void*);
|
|
void* Arg;
|
|
uint32_t Pid; // owner pid, for the retval table
|
|
uint32_t Tid; // filled in by pthread_create after KeCreateThread returns
|
|
} PTHREAD_TRAMP;
|
|
|
|
static void PthreadTrampoline(uintptr_t raw) {
|
|
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)raw;
|
|
void* (*fn)(void*) = tr->Fn;
|
|
void* arg = tr->Arg;
|
|
uint32_t pid = tr->Pid;
|
|
uint32_t tid = tr->Tid;
|
|
SysFree(tr, sizeof(PTHREAD_TRAMP));
|
|
|
|
void* retval = fn(arg);
|
|
|
|
// Store retval for pthread_join. If the slot isn't there (join was called
|
|
// before the thread was even created, which shouldn't happen) just drop it.
|
|
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
|
|
if (slot) slot->RetVal = retval;
|
|
|
|
SysExitThread(0);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// pthread API
|
|
// ---------------------------------------------------------------------------
|
|
|
|
int pthread_create(pthread_t* t, const pthread_attr_t* attr,
|
|
void* (*fn)(void*), void* arg) {
|
|
(void)attr;
|
|
|
|
uint32_t pid = SysGetCurrentPid();
|
|
|
|
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)SysAlloc(sizeof(PTHREAD_TRAMP));
|
|
if (!tr) return ENOMEM;
|
|
tr->Fn = fn;
|
|
tr->Arg = arg;
|
|
tr->Pid = pid;
|
|
tr->Tid = 0; // filled below
|
|
|
|
uint32_t tid = 0;
|
|
uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline,
|
|
(uint64_t)tr, 1 /*PriorityNormal*/, &tid);
|
|
if (s != 0) {
|
|
SysFree(tr, sizeof(PTHREAD_TRAMP));
|
|
return EAGAIN;
|
|
}
|
|
tr->Tid = tid; // safe: trampoline reads Tid after SysCreateThread returns
|
|
|
|
// Reserve a retval slot before the trampoline can exit
|
|
PthreadSlotAlloc(pid, tid);
|
|
|
|
t->Tid = tid;
|
|
return 0;
|
|
}
|
|
|
|
int pthread_join(pthread_t t, void** retval) {
|
|
uint32_t pid = SysGetCurrentPid();
|
|
uint32_t s = SysWaitThread(t.Tid);
|
|
// STATUS_NOT_FOUND (0x22) means already dead - treat as success
|
|
if (s != 0 && s != 0x0022) return ESRCH;
|
|
|
|
PTHREAD_SLOT* slot = PthreadSlotFind(pid, t.Tid);
|
|
if (retval) *retval = slot ? slot->RetVal : (void*)0;
|
|
if (slot) slot->Used = false;
|
|
return 0;
|
|
}
|
|
|
|
void pthread_exit(void* retval) {
|
|
uint32_t pid = SysGetCurrentPid();
|
|
uint32_t tid = SysGetCurrentTid();
|
|
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
|
|
if (slot) slot->RetVal = retval;
|
|
SysExitThread(0);
|
|
while (1); // unreachable
|
|
}
|
|
|
|
int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) {
|
|
(void)attr;
|
|
uint32_t h;
|
|
uint32_t s = SysMutexCreate(&h);
|
|
if (s != 0) return ENOMEM;
|
|
m->Handle = h;
|
|
return 0;
|
|
}
|
|
|
|
int pthread_mutex_destroy(pthread_mutex_t* m) {
|
|
SysCloseHandle(m->Handle);
|
|
m->Handle = 0;
|
|
return 0;
|
|
}
|
|
|
|
int pthread_mutex_lock(pthread_mutex_t* m) {
|
|
uint32_t s = SysMutexAcquire(m->Handle);
|
|
return s == 0 ? 0 : EINVAL;
|
|
}
|
|
|
|
int pthread_mutex_trylock(pthread_mutex_t* m) {
|
|
uint32_t s = SysMutexTryAcquire(m->Handle);
|
|
return s == 0 ? 0 : EAGAIN;
|
|
}
|
|
|
|
int pthread_mutex_unlock(pthread_mutex_t* m) {
|
|
uint32_t s = SysMutexRelease(m->Handle);
|
|
return s == 0 ? 0 : EINVAL;
|
|
}
|