// posix_thread.c - pthread_create, pthread_join, pthread_exit, pthread_mutex_* #include #include #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); SysFree(tr, sizeof(PTHREAD_TRAMP)); 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*)SysAlloc(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) { SysFree(tr, sizeof(PTHREAD_TRAMP)); 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; }