149 lines
4.5 KiB
C
149 lines
4.5 KiB
C
// posix_thread.c - pthread_create, pthread_join, pthread_exit, pthread_mutex_*
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <ntlite/syscall.h>
|
|
#include "posix.h"
|
|
|
|
// 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
|
|
} 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 = SysGetCurrentTid();
|
|
|
|
// Create the join slot from inside the new thread. KeCreateThread makes the
|
|
// thread runnable before the creator regains control, so the old design
|
|
// raced on tr->Tid and could observe zero/stale memory.
|
|
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
|
|
if (!slot) slot = PthreadSlotAlloc(pid, tid);
|
|
|
|
free(tr);
|
|
|
|
void* retval = fn(arg);
|
|
|
|
// Store retval for pthread_join.
|
|
if (slot) slot->RetVal = retval;
|
|
|
|
SysExitThread(0);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// pthread API
|
|
// ---------------------------------------------------------------------------
|
|
|
|
__declspec(dllexport) 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*)malloc(sizeof(PTHREAD_TRAMP));
|
|
if (!tr) return ENOMEM;
|
|
tr->Fn = fn;
|
|
tr->Arg = arg;
|
|
tr->Pid = pid;
|
|
|
|
uint32_t tid = 0;
|
|
uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline,
|
|
(uint64_t)tr, 1 /*PriorityNormal*/, &tid);
|
|
if (s != 0) {
|
|
free(tr);
|
|
return EAGAIN;
|
|
}
|
|
// The trampoline allocates the retval slot itself after it knows its real
|
|
// TID, avoiding the creator/new-thread race.
|
|
if (t) t->Tid = tid;
|
|
return 0;
|
|
}
|
|
|
|
__declspec(dllexport) 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;
|
|
}
|
|
|
|
__declspec(dllexport) 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
|
|
}
|
|
|
|
__declspec(dllexport) 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;
|
|
}
|
|
|
|
__declspec(dllexport) int pthread_mutex_destroy(pthread_mutex_t* m) {
|
|
SysCloseHandle(m->Handle);
|
|
m->Handle = 0;
|
|
return 0;
|
|
}
|
|
|
|
__declspec(dllexport) int pthread_mutex_lock(pthread_mutex_t* m) {
|
|
uint32_t s = SysMutexAcquire(m->Handle);
|
|
return s == 0 ? 0 : EINVAL;
|
|
}
|
|
|
|
__declspec(dllexport) int pthread_mutex_trylock(pthread_mutex_t* m) {
|
|
uint32_t s = SysMutexTryAcquire(m->Handle);
|
|
return s == 0 ? 0 : EAGAIN;
|
|
}
|
|
|
|
__declspec(dllexport) int pthread_mutex_unlock(pthread_mutex_t* m) {
|
|
uint32_t s = SysMutexRelease(m->Handle);
|
|
return s == 0 ? 0 : EINVAL;
|
|
}
|