Files
nt-lite/code/posix/posix_thread.c
T

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;
}