metal-cos/lib/libc/posix/pthread.c
2015-02-03 14:26:51 -08:00

468 lines
7.8 KiB
C

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <core/mutex.h>
#include <syscall.h>
#include <sys/syscall.h>
struct pthread_attr {
uint64_t _unused;
};
struct pthread {
uint64_t tid;
int errno;
TAILQ_ENTRY(pthread) threadTable;
// Initialization
void *(*entry)(void *);
void *arg;
// Termination
void *result;
// Condition Variables
TAILQ_ENTRY(pthread) cvTable;
};
typedef TAILQ_HEAD(pthreadList, pthread) pthreadList;
#define THREAD_HASH_SLOTS 32
static CoreMutex __threadTableLock;
static pthreadList __threads[THREAD_HASH_SLOTS];
void
__pthread_init(void)
{
int i;
struct pthread *thr = (struct pthread *)malloc(sizeof(*thr));
for (i = 0; i < THREAD_HASH_SLOTS; i++) {
TAILQ_INIT(&__threads[i]);
}
if (thr == NULL) {
abort();
}
thr->tid = OSGetTID();
thr->entry = NULL;
thr->arg = 0;
CoreMutex_Init(&__threadTableLock);
CoreMutex_Lock(&__threadTableLock);
TAILQ_INSERT_HEAD(&__threads[thr->tid % THREAD_HASH_SLOTS], thr, threadTable);
CoreMutex_Unlock(&__threadTableLock);
}
pthread_t
pthread_self(void)
{
int tid = OSGetTID();
struct pthread *thr;
CoreMutex_Lock(&__threadTableLock);
TAILQ_FOREACH(thr, &__threads[tid % THREAD_HASH_SLOTS], threadTable) {
if (thr->tid == tid) {
CoreMutex_Unlock(&__threadTableLock);
return thr;
}
}
CoreMutex_Unlock(&__threadTableLock);
printf("pthread_self failed to find current thread!\n");
abort();
}
void
pthreadCreateHelper(void *arg)
{
struct pthread *thr = (struct pthread *)arg;
thr->result = (thr->entry)(thr->arg);
OSThreadExit(0);
}
int
pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg)
{
uint64_t status;
struct pthread *thr;
thr = malloc(sizeof(*thr));
if (!thr) {
return EAGAIN;
}
memset(thr, 0, sizeof(*thr));
thr->entry = start_routine;
thr->arg = arg;
status = OSThreadCreate((uintptr_t)&pthreadCreateHelper, (uint64_t)thr);
if (SYSCALL_ERRCODE(status) != 0) {
free(thr);
return SYSCALL_ERRCODE(status);
}
thr->tid = SYSCALL_VALUE(status);
CoreMutex_Lock(&__threadTableLock);
TAILQ_INSERT_HEAD(&__threads[thr->tid % THREAD_HASH_SLOTS], thr, threadTable);
CoreMutex_Unlock(&__threadTableLock);
*thread = thr;
return 0;
}
void
pthread_exit(void *value_ptr)
{
struct pthread *thr = pthread_self();
thr->result = value_ptr;
OSThreadExit(0);
}
int
pthread_join(pthread_t thread, void **value_ptr)
{
struct pthread *thr = thread;
uint64_t status = OSThreadWait(0);//thr->tid);
if (SYSCALL_ERRCODE(status) != 0) {
return status;
}
*value_ptr = (void *)thr->result;
CoreMutex_Lock(&__threadTableLock);
TAILQ_REMOVE(&__threads[thr->tid % THREAD_HASH_SLOTS], thr, threadTable);
CoreMutex_Unlock(&__threadTableLock);
// Cleanup
free(thr);
return 0;
}
void
pthread_yield(void)
{
OSThreadSleep(0);
}
/*
* Barriers
*/
int
pthread_barrier_init(pthread_barrier_t *barrier,
const pthread_barrierattr_t *attr,
unsigned count)
{
return EINVAL;
}
int
pthread_barrier_destroy(pthread_barrier_t *barrier)
{
return EINVAL;
}
int
pthread_barrier_wait(pthread_barrier_t *barrier)
{
return EINVAL;
}
/*
* Mutex
*/
struct pthread_mutexattr {
uint64_t _unused;
};
struct pthread_mutex {
uint64_t lock;
};
int
pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
{
struct pthread_mutex *mtx = (struct pthread_mutex *)malloc(sizeof(*mtx));
if (mtx == NULL) {
return ENOMEM;
}
mtx->lock = 0;
*mutex = mtx;
return 0;
}
int
pthread_mutex_destroy(pthread_mutex_t *mutex)
{
struct pthread_mutex *mtx = *mutex;
if (mtx == NULL) {
return EINVAL;
} else if (mtx->lock == 1) {
return EBUSY;
} else {
*mutex = NULL;
free(mtx);
return 0;
}
}
int
pthread_mutex_lock(pthread_mutex_t *mutex)
{
struct pthread_mutex *mtx;
if (*mutex == NULL) {
int status = pthread_mutex_init(mutex, NULL);
if (status != 0)
return status;
}
mtx = *mutex;
// XXX: Should have the kernel wait kernel instead of yielding
while (__sync_lock_test_and_set(&mtx->lock, 1) == 1) {
OSThreadSleep(0);
}
return 0;
}
int
pthread_mutex_trylock(pthread_mutex_t *mutex)
{
struct pthread_mutex *mtx;
if (*mutex == NULL) {
int status = pthread_mutex_init(mutex, NULL);
if (status != 0)
return status;
}
mtx = *mutex;
if (__sync_lock_test_and_set(&mtx->lock, 1) == 1) {
return EBUSY;
} else {
// Lock acquired
return 0;
}
}
int
pthread_mutex_unlock(pthread_mutex_t *mutex)
{
struct pthread_mutex *mtx;
if (*mutex == NULL) {
int status = pthread_mutex_init(mutex, NULL);
if (status != 0)
return status;
}
mtx = *mutex;
__sync_lock_release(&mtx->lock);
// XXX: Wakeup a sleeping thread
return 0;
}
/*
* Reader/Writer Lock
*/
/*
* Condition Variables
*/
struct pthread_condattr {
uint64_t _unused;
};
struct pthread_cond {
CoreMutex mtx;
uint64_t enter;
uint64_t exit;
};
int
pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
{
struct pthread_cond *cnd = (struct pthread_cond *)malloc(sizeof(*cnd));
if (cnd == NULL) {
return ENOMEM;
}
CoreMutex_Init(&cnd->mtx);
cnd->enter = 0;
cnd->exit = 0;
*cond = cnd;
return 0;
}
int
pthread_cond_destroy(pthread_cond_t *cond)
{
struct pthread_cond *cnd = *cond;
*cond = NULL;
free(cnd);
return 0;
}
int
pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
int status;
struct pthread_cond *cnd;
uint64_t level;
if (*cond == NULL) {
status = pthread_cond_init(cond, NULL);
if (status != 0)
return status;
}
cnd = *cond;
if (mutex) {
status = pthread_mutex_unlock(mutex);
if (status != 0)
return status;
}
CoreMutex_Lock(&cnd->mtx);
level = cnd->enter;
cnd->enter++;
CoreMutex_Unlock(&cnd->mtx);
while (level >= cnd->exit) {
OSThreadSleep(0);
}
if (mutex) {
status = pthread_mutex_lock(mutex);
if (status != 0)
return status;
}
return 0;
}
int
pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abstime)
{
int status = 0;
int rstatus = 0;
struct pthread_cond *cnd;
uint64_t level;
uint64_t endtime = abstime->tv_sec * 1000000000 + abstime->tv_nsec;
if (*cond == NULL) {
status = pthread_cond_init(cond, NULL);
if (status != 0)
return status;
}
cnd = *cond;
if (mutex) {
status = pthread_mutex_unlock(mutex);
if (status != 0)
return status;
}
CoreMutex_Lock(&cnd->mtx);
level = cnd->enter;
cnd->enter++;
CoreMutex_Unlock(&cnd->mtx);
while (level >= cnd->exit) {
OSThreadSleep(0);
if (endtime < OSTime()) {
rstatus = ETIMEDOUT;
break;
}
}
if (mutex) {
status = pthread_mutex_lock(mutex);
if (status != 0)
return status;
}
return rstatus;
}
int
pthread_cond_signal(pthread_cond_t *cond)
{
struct pthread_cond *cnd;
if (*cond == NULL) {
int status = pthread_cond_init(cond, NULL);
if (status != 0)
return status;
}
cnd = *cond;
CoreMutex_Lock(&cnd->mtx);
cnd->exit++;
CoreMutex_Unlock(&cnd->mtx);
return 0;
}
int
pthread_cond_broadcast(pthread_cond_t *cond)
{
struct pthread_cond *cnd;
if (*cond == NULL) {
int status = pthread_cond_init(cond, NULL);
if (status != 0)
return status;
}
cnd = *cond;
CoreMutex_Lock(&cnd->mtx);
cnd->exit = cnd->enter;
CoreMutex_Unlock(&cnd->mtx);
return 0;
}