Viewing: pthread.c
📄 pthread.c (Read Only) ⬅ To go back
#include "pthread.h"
#include "syscall.h"
#include "errno.h"

#include <stdint.h>
#include <stddef.h>

/* clone() flags (must match kernel's process.h) */
#define CLONE_VM        0x00000100
#define CLONE_FS        0x00000200
#define CLONE_FILES     0x00000400
#define CLONE_SIGHAND   0x00000800
#define CLONE_THREAD    0x00010000
#define CLONE_SETTLS    0x00080000
#define CLONE_PARENT_SETTID  0x00100000
#define CLONE_CHILD_CLEARTID 0x00200000

#define CLONE_THREAD_FLAGS  (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SETTLS)

#define THREAD_STACK_SIZE 8192

/* Per-thread trampoline info, placed at the bottom of the thread stack */
struct thread_start_info {
    void* (*start_routine)(void*);
    void* arg;
    void* retval;
    int   exited;
};

/* Wrapper that runs on the new thread's stack */
static void __attribute__((noreturn, used))
_pthread_trampoline(struct thread_start_info* info) {
    void* ret = info->start_routine(info->arg);
    info->retval = ret;
    info->exited = 1;
    /* Exit thread */
    _syscall1(SYS_EXIT, 0);
    for (;;) __asm__ volatile("nop");
}

/* Simple bump allocator for thread stacks (no free support yet) */
static uint8_t _thread_stack_pool[8][THREAD_STACK_SIZE];
static int _thread_stack_next = 0;

static void* alloc_thread_stack(void) {
    if (_thread_stack_next >= 8) return NULL;
    return &_thread_stack_pool[_thread_stack_next++];
}

int pthread_create(pthread_t* thread, const pthread_attr_t* attr,
                   void* (*start_routine)(void*), void* arg) {
    (void)attr;
    if (!thread || !start_routine) return 22; /* EINVAL */

    /* Allocate a stack for the new thread */
    void* stack_base = alloc_thread_stack();
    if (!stack_base) return 12; /* ENOMEM */

    /* Place start_info at the bottom of the stack */
    struct thread_start_info* info = (struct thread_start_info*)stack_base;
    info->start_routine = start_routine;
    info->arg = arg;
    info->retval = NULL;
    info->exited = 0;

    /* Set up the child stack: top of allocated region, with trampoline args */
    uint32_t* sp = (uint32_t*)((uint8_t*)stack_base + THREAD_STACK_SIZE);

    /* Push argument (pointer to start_info) for trampoline */
    *--sp = (uint32_t)(uintptr_t)info;
    /* Push fake return address */
    *--sp = 0;

    /* clone(flags, child_stack, parent_tidptr, tls, child_tidptr)
     * syscall args: eax=SYS_CLONE, ebx=flags, ecx=child_stack,
     *               edx=parent_tidptr, esi=tls, edi=child_tidptr
     *
     * We use _syscall5 but note: parent_tidptr and child_tidptr are 0 for now,
     * tls is 0 (no TLS for basic threads). */
    uint32_t flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD;
    int ret = _syscall5(SYS_CLONE, (int)flags, (int)(uintptr_t)sp, 0, 0, 0);

    if (ret < 0) {
        /* clone failed */
        return -ret;
    }

    if (ret == 0) {
        /* We are the child thread.
         * Pop the info pointer from stack and call trampoline. */
        struct thread_start_info* my_info;
        __asm__ volatile("popl %0" : "=r"(my_info));
        /* Discard fake return address */
        __asm__ volatile("addl $4, %%esp" : : : "memory");
        _pthread_trampoline(my_info);
        /* Never reached */
    }

    /* Parent: ret is child tid */
    *thread = (pthread_t)(uint32_t)ret;
    return 0;
}

int pthread_join(pthread_t thread, void** retval) {
    /* Use waitpid on the thread's PID.
     * Since CLONE_THREAD threads are in the same thread group,
     * waitpid may not work directly. Use a simple spin-wait as fallback. */
    int status = 0;
    int r = _syscall3(SYS_WAITPID, (int)thread, (int)&status, 0);
    if (r < 0) {
        /* Thread may have already exited or waitpid doesn't work for threads.
         * For now, just return success if we can't wait. */
        (void)retval;
        return 0;
    }
    if (retval) *retval = NULL;
    return 0;
}

void pthread_exit(void* retval) {
    (void)retval;
    _syscall1(SYS_EXIT, 0);
    for (;;) __asm__ volatile("nop");
}

pthread_t pthread_self(void) {
    return (pthread_t)(uint32_t)_syscall0(SYS_GETTID);
}

int pthread_attr_init(pthread_attr_t* attr) {
    if (!attr) return 22;
    attr->stack_size = THREAD_STACK_SIZE;
    attr->detach_state = PTHREAD_CREATE_JOINABLE;
    return 0;
}

int pthread_attr_destroy(pthread_attr_t* attr) {
    (void)attr;
    return 0;
}

int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize) {
    if (!attr || stacksize < 4096) return 22;
    attr->stack_size = stacksize;
    return 0;
}

int pthread_detach(pthread_t thread) {
    (void)thread;
    return 0;
}

int pthread_cancel(pthread_t thread) {
    (void)thread;
    return 0;
}

int pthread_setcancelstate(int state, int* oldstate) {
    if (oldstate) *oldstate = 0;
    (void)state;
    return 0;
}

int pthread_setcanceltype(int type, int* oldtype) {
    if (oldtype) *oldtype = 0;
    (void)type;
    return 0;
}

void pthread_testcancel(void) {}

/* ---- Futex helpers ---- */
#define FUTEX_WAIT 0
#define FUTEX_WAKE 1

static int futex_wait(volatile int* addr, int val) {
    return _syscall3(SYS_FUTEX, (int)addr, FUTEX_WAIT, val);
}

static int futex_wake(volatile int* addr, int count) {
    return _syscall3(SYS_FUTEX, (int)addr, FUTEX_WAKE, count);
}

static int atomic_cas(volatile int* ptr, int old, int new_val) {
    int prev;
    __asm__ volatile("lock cmpxchgl %2, %1"
                     : "=a"(prev), "+m"(*ptr)
                     : "r"(new_val), "0"(old)
                     : "memory");
    return prev;
}

static int atomic_xchg(volatile int* ptr, int val) {
    __asm__ volatile("xchgl %0, %1"
                     : "=r"(val), "+m"(*ptr)
                     : "0"(val)
                     : "memory");
    return val;
}

static void atomic_add(volatile int* ptr, int val) {
    __asm__ volatile("lock addl %1, %0" : "+m"(*ptr) : "r"(val) : "memory");
}

/* ---- Mutex ---- */
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) {
    if (!mutex) return 22;
    mutex->__lock = 0;
    mutex->__owner = 0;
    mutex->__type = attr ? attr->__type : PTHREAD_MUTEX_NORMAL;
    mutex->__count = 0;
    return 0;
}

int pthread_mutex_destroy(pthread_mutex_t* mutex) {
    (void)mutex;
    return 0;
}

int pthread_mutex_lock(pthread_mutex_t* mutex) {
    if (!mutex) return 22;
    int tid = _syscall0(SYS_GETTID);

    if (mutex->__type == PTHREAD_MUTEX_RECURSIVE && mutex->__owner == tid) {
        mutex->__count++;
        return 0;
    }

    while (atomic_xchg(&mutex->__lock, 1) != 0) {
        futex_wait(&mutex->__lock, 1);
    }
    mutex->__owner = tid;
    mutex->__count = 1;
    return 0;
}

int pthread_mutex_trylock(pthread_mutex_t* mutex) {
    if (!mutex) return 22;
    int tid = _syscall0(SYS_GETTID);

    if (mutex->__type == PTHREAD_MUTEX_RECURSIVE && mutex->__owner == tid) {
        mutex->__count++;
        return 0;
    }

    if (atomic_cas(&mutex->__lock, 0, 1) == 0) {
        mutex->__owner = tid;
        mutex->__count = 1;
        return 0;
    }
    return 16; /* EBUSY */
}

int pthread_mutex_unlock(pthread_mutex_t* mutex) {
    if (!mutex) return 22;

    if (mutex->__type == PTHREAD_MUTEX_RECURSIVE) {
        if (--mutex->__count > 0) return 0;
    }

    mutex->__owner = 0;
    mutex->__count = 0;
    atomic_xchg(&mutex->__lock, 0);
    futex_wake(&mutex->__lock, 1);
    return 0;
}

int pthread_mutexattr_init(pthread_mutexattr_t* attr) {
    if (!attr) return 22;
    attr->__type = PTHREAD_MUTEX_NORMAL;
    return 0;
}

int pthread_mutexattr_destroy(pthread_mutexattr_t* attr) {
    (void)attr;
    return 0;
}

int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type) {
    if (!attr) return 22;
    attr->__type = type;
    return 0;
}

int pthread_mutexattr_gettype(const pthread_mutexattr_t* attr, int* type) {
    if (!attr || !type) return 22;
    *type = attr->__type;
    return 0;
}

/* ---- Condition Variable ---- */
int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr) {
    (void)attr;
    if (!cond) return 22;
    cond->__seq = 0;
    return 0;
}

int pthread_cond_destroy(pthread_cond_t* cond) {
    (void)cond;
    return 0;
}

int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) {
    int seq = cond->__seq;
    pthread_mutex_unlock(mutex);
    futex_wait(&cond->__seq, seq);
    pthread_mutex_lock(mutex);
    return 0;
}

int pthread_cond_signal(pthread_cond_t* cond) {
    atomic_add(&cond->__seq, 1);
    futex_wake(&cond->__seq, 1);
    return 0;
}

int pthread_cond_broadcast(pthread_cond_t* cond) {
    atomic_add(&cond->__seq, 1);
    futex_wake(&cond->__seq, 0x7FFFFFFF);
    return 0;
}

/* ---- Read-Write Lock ---- */
int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr) {
    (void)attr;
    if (!rwlock) return 22;
    rwlock->__readers = 0;
    rwlock->__writer = 0;
    return 0;
}

int pthread_rwlock_destroy(pthread_rwlock_t* rwlock) {
    (void)rwlock;
    return 0;
}

int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock) {
    while (1) {
        while (rwlock->__writer)
            futex_wait(&rwlock->__writer, 1);
        atomic_add(&rwlock->__readers, 1);
        if (!rwlock->__writer) return 0;
        atomic_add(&rwlock->__readers, -1);
    }
}

int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock) {
    while (atomic_xchg(&rwlock->__writer, 1) != 0)
        futex_wait(&rwlock->__writer, 1);
    while (rwlock->__readers > 0)
        futex_wait(&rwlock->__readers, rwlock->__readers);
    return 0;
}

int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock) {
    if (rwlock->__writer) return 16; /* EBUSY */
    atomic_add(&rwlock->__readers, 1);
    if (rwlock->__writer) {
        atomic_add(&rwlock->__readers, -1);
        return 16;
    }
    return 0;
}

int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock) {
    if (atomic_cas(&rwlock->__writer, 0, 1) != 0) return 16;
    if (rwlock->__readers > 0) {
        rwlock->__writer = 0;
        return 16;
    }
    return 0;
}

int pthread_rwlock_unlock(pthread_rwlock_t* rwlock) {
    if (rwlock->__writer) {
        rwlock->__writer = 0;
        futex_wake(&rwlock->__writer, 0x7FFFFFFF);
    } else {
        atomic_add(&rwlock->__readers, -1);
        if (rwlock->__readers == 0)
            futex_wake(&rwlock->__readers, 1);
    }
    return 0;
}

/* ---- Thread-Specific Data ---- */
#define PTHREAD_KEYS_MAX 32
static void* _tsd_values[PTHREAD_KEYS_MAX];
static void (*_tsd_destructors[PTHREAD_KEYS_MAX])(void*);
static int _tsd_used[PTHREAD_KEYS_MAX];
static int _tsd_next_key = 0;

int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)) {
    if (!key) return 22;
    if (_tsd_next_key >= PTHREAD_KEYS_MAX) return 11; /* EAGAIN */
    int k = _tsd_next_key++;
    _tsd_used[k] = 1;
    _tsd_destructors[k] = destructor;
    _tsd_values[k] = (void*)0;
    *key = (pthread_key_t)k;
    return 0;
}

int pthread_key_delete(pthread_key_t key) {
    if (key >= PTHREAD_KEYS_MAX || !_tsd_used[key]) return 22;
    _tsd_used[key] = 0;
    _tsd_destructors[key] = (void*)0;
    _tsd_values[key] = (void*)0;
    return 0;
}

void* pthread_getspecific(pthread_key_t key) {
    if (key >= PTHREAD_KEYS_MAX) return (void*)0;
    return _tsd_values[key];
}

int pthread_setspecific(pthread_key_t key, const void* value) {
    if (key >= PTHREAD_KEYS_MAX) return 22;
    _tsd_values[key] = (void*)(uintptr_t)value;
    return 0;
}

/* ---- Once ---- */
int pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) {
    if (!once_control || !init_routine) return 22;
    if (atomic_cas(once_control, 0, 1) == 0) {
        init_routine();
        *once_control = 2;
        futex_wake(once_control, 0x7FFFFFFF);
    } else {
        while (*once_control == 1)
            futex_wait(once_control, 1);
    }
    return 0;
}

/* ---- Barrier ---- */
int pthread_barrier_init(pthread_barrier_t* barrier,
                         const pthread_barrierattr_t* attr, unsigned count) {
    (void)attr;
    if (!barrier || count == 0) return 22;
    barrier->__count = 0;
    barrier->__total = (int)count;
    barrier->__seq = 0;
    return 0;
}

int pthread_barrier_destroy(pthread_barrier_t* barrier) {
    (void)barrier;
    return 0;
}

int pthread_barrier_wait(pthread_barrier_t* barrier) {
    int seq = barrier->__seq;
    int n;
    atomic_add(&barrier->__count, 1);
    n = barrier->__count;
    if (n >= barrier->__total) {
        barrier->__count = 0;
        atomic_add(&barrier->__seq, 1);
        futex_wake(&barrier->__seq, 0x7FFFFFFF);
        return PTHREAD_BARRIER_SERIAL_THREAD;
    }
    while (barrier->__seq == seq)
        futex_wait(&barrier->__seq, seq);
    return 0;
}