summaryrefslogtreecommitdiff
path: root/core/thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/thread.c')
-rw-r--r--core/thread.c192
1 files changed, 192 insertions, 0 deletions
diff --git a/core/thread.c b/core/thread.c
new file mode 100644
index 0000000..59f50b5
--- /dev/null
+++ b/core/thread.c
@@ -0,0 +1,192 @@
+#include <rune/core/thread.h>
+#include <rune/core/logging.h>
+#include <rune/core/alloc.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdatomic.h>
+
+static struct thread *threads = NULL;
+static struct mutex *mutexes = NULL;
+static int next_tid = 0;
+static int next_mid = 0;
+
+struct start_args {
+ struct thread *thread;
+ void* (*thread_fn)(void*);
+ void *thread_args;
+};
+
+struct thread* _find_thread_by_handle(void *handle) {
+ if (threads == NULL)
+ return NULL;
+
+ struct list_head *temp = &threads->list;
+ struct thread *ret;
+ while (temp != NULL) {
+ ret = (struct thread*)((void*)temp - offsetof(struct thread, list));
+ if (pthread_equal(*(pthread_t*)ret->thread_handle, *(pthread_t*)handle) == 1)
+ return ret;
+ temp = temp->next;
+ }
+ return NULL;
+}
+
+struct thread* _find_thread_by_id(int ID) {
+ if (threads == NULL)
+ return NULL;
+
+ struct list_head *temp = &threads->list;
+ struct thread *ret;
+ while (temp != NULL) {
+ ret = (struct thread*)((void*)temp - offsetof(struct thread, list));
+ if (ret->ID == ID)
+ return ret;
+ temp = temp->next;
+ }
+ return NULL;
+}
+
+struct mutex* _find_mutex_by_id(int ID) {
+ if (mutexes == NULL)
+ return NULL;
+
+ struct list_head *temp = &mutexes->list;
+ struct mutex *ret;
+ while (temp != NULL) {
+ ret = (struct mutex*)((void*)temp - offsetof(struct mutex, list));
+ if (ret->ID == ID)
+ return ret;
+ temp = temp->next;
+ }
+ return NULL;
+}
+
+
+void _cleanup_pthread(void *arg) {
+ struct thread *thread = (struct thread*)arg;
+ list_del(&thread->list);
+ rune_free(thread->thread_handle);
+ rune_free(thread);
+}
+
+void* _startup_pthread(void *arg) {
+ struct start_args *start_args = (struct start_args*)arg;
+
+ void* (*thread_fn)(void *data) = start_args->thread_fn;
+ pthread_cleanup_push(_cleanup_pthread, start_args->thread);
+ if (thread_fn != NULL)
+ (*thread_fn)(start_args->thread_args);
+ pthread_cleanup_pop(1);
+ return NULL;
+}
+
+void rune_init_thread_api(void) {
+ threads = rune_alloc(sizeof(struct thread));
+ threads->ID = next_tid++;
+ threads->detached = 0;
+ threads->thread_handle = rune_alloc(sizeof(pthread_t));
+ *(pthread_t*)threads->thread_handle = pthread_self();
+ pthread_cleanup_push(_cleanup_pthread, threads);
+ pthread_cleanup_pop(0);
+}
+
+int rune_thread_init(void* (*thread_fn)(void *data), void *data, int detached) {
+ struct thread *thread = rune_alloc(sizeof(struct thread));
+ thread->ID = next_tid++;
+ thread->detached = detached;
+ thread->thread_handle = rune_alloc(sizeof(pthread_t));
+ list_add(&thread->list, &threads->list);
+
+ struct start_args *args = rune_alloc(sizeof(struct start_args));
+ args->thread = thread;
+ args->thread_fn = thread_fn;
+ args->thread_args = data;
+ int retval = pthread_create(thread->thread_handle, NULL, _startup_pthread, args);
+ rune_free(args);
+ if (retval != 0) {
+ log_output(LOG_ERROR, "Thread creation failed: %s", strerror(retval));
+ return -1;
+ }
+ log_output(LOG_DEBUG, "Initialized new thread with ID=%d", thread->ID);
+ if (detached == 1) {
+ log_output(LOG_DEBUG, "Thread %d has been detached, join no longer possible");
+ pthread_detach(*(pthread_t*)thread->thread_handle);
+ }
+ return thread->ID;
+}
+
+int rune_thread_cancel(int ID) {
+ log_output(LOG_DEBUG, "Received cancel request for thread %d", ID);
+ struct thread *thread = _find_thread_by_id(ID);
+ if (thread == NULL) {
+ log_output(LOG_ERROR, "Thread %d does not exist", ID);
+ return -1;
+ }
+ pthread_cancel(*((pthread_t*)thread->thread_handle));
+ return 0;
+}
+
+int rune_thread_join(int ID, void **retval) {
+ struct thread *thread = _find_thread_by_id(ID);
+ if (thread == NULL)
+ return 0;
+
+ if (thread->detached == 1) {
+ log_output(LOG_ERROR, "Cannot join thread %d, detached", thread->ID);
+ return -1;
+ }
+ pthread_join(*((pthread_t*)thread->thread_handle), retval);
+ return 0;
+}
+
+int rune_thread_self(void) {
+ pthread_t cur = pthread_self();
+ struct thread *ret = _find_thread_by_handle((void*)&cur);
+ if (ret != NULL)
+ return ret->ID;
+ return -1;
+}
+
+void rune_thread_exit(void *retval) {
+ pthread_t cur = pthread_self();
+ struct thread *self = _find_thread_by_handle((void*)&cur);
+ log_output(LOG_DEBUG, "Thread %d called thread_exit", self->ID);
+ pthread_exit(retval);
+}
+
+int rune_mutex_init(void) {
+ struct mutex *mutex = rune_alloc(sizeof(struct mutex));
+ mutex->ID = next_mid++;
+ mutex->mutex_handle = rune_alloc(sizeof(pthread_mutex_t));
+ pthread_mutex_init((pthread_mutex_t*)mutex->mutex_handle, NULL);
+ if (mutexes == NULL)
+ mutexes = mutex;
+ else
+ list_add(&mutex->list, &mutexes->list);
+ return mutex->ID;
+}
+
+int rune_mutex_destroy(int ID) {
+ struct mutex *mutex = _find_mutex_by_id(ID);
+ rune_free(mutex->mutex_handle);
+ list_del(&mutex->list);
+ rune_free(mutex);
+}
+
+int rune_mutex_lock(int ID) {
+ struct mutex *mutex = _find_mutex_by_id(ID);
+ int retval = pthread_mutex_lock((pthread_mutex_t*)mutex->mutex_handle);
+ if (retval != 0) {
+ char *str = strerror(retval);
+ log_output(LOG_ERROR, "Cannot lock mutex %d: %s", mutex->ID, str);
+ }
+}
+
+int rune_mutex_unlock(int ID) {
+ struct mutex *mutex = _find_mutex_by_id(ID);
+ int retval = pthread_mutex_unlock((pthread_mutex_t*)mutex->mutex_handle);
+ if (retval != 0) {
+ char *str = strerror(retval);
+ log_output(LOG_ERROR, "Cannot unlock mutex %d: %s", mutex->ID, str);
+ }
+}