summaryrefslogtreecommitdiff
path: root/engine/core
diff options
context:
space:
mode:
Diffstat (limited to 'engine/core')
-rw-r--r--engine/core/abort.c93
-rw-r--r--engine/core/alloc.c178
-rw-r--r--engine/core/callbacks.c27
-rw-r--r--engine/core/init.c48
-rw-r--r--engine/core/logging.c114
-rw-r--r--engine/core/mod.c90
-rw-r--r--engine/core/thread.c218
7 files changed, 768 insertions, 0 deletions
diff --git a/engine/core/abort.c b/engine/core/abort.c
new file mode 100644
index 0000000..5ee42f6
--- /dev/null
+++ b/engine/core/abort.c
@@ -0,0 +1,93 @@
+/*
+ * Rune Game Engine
+ * Copyright 2024 Danny Holman <dholman@gymli.org>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include <rune/core/abort.h>
+#include <rune/core/alloc.h>
+#include <rune/core/init.h>
+#include <rune/core/logging.h>
+#include <rune/util/exits.h>
+#include <stdlib.h>
+
+#define MAX_TRACE_ITEMS 30
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <dbghelp.h>
+
+void _stack_trace(void) {
+ void* buffer[MAX_TRACE_ITEMS];
+ HANDLE process = GetCurrentProcess();
+
+ SymInitialize(process, NULL, TRUE);
+ int num_links = CaptureStackBackTrace(0, MAX_TRACE_ITEMS, buffer, NULL);
+ SYMBOL_INFO *symbol = (SYMBOL_INFO*)rune_alloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char));
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = 255;
+
+ for (int i = 0; i < num_links; i++) {
+ SymFromAddr(process, (DWORD64)(buffer[i]), 0, symbol);
+ log_output(LOG_INFO, "#%d: %s", i, symbol->Name, symbol->Address);
+ }
+
+ rune_free(symbol);
+ SymCleanup(process);
+}
+
+#else
+
+#include <execinfo.h>
+
+void _stack_trace(void) {
+ void* buffer[MAX_TRACE_ITEMS];
+ int num_links = backtrace(buffer, MAX_TRACE_ITEMS);
+ char** symbuf = backtrace_symbols(buffer, num_links);
+
+ for (int i = 0; i < num_links; i++)
+ log_output(LOG_INFO, "#%d: %s", i, symbuf[i]);
+
+ free(symbuf);
+}
+
+#endif
+
+NORET void rune_abort(void) {
+ log_output(LOG_INFO, "Abort called, printing stack trace");
+ _stack_trace();
+ rune_exit();
+ exit(REXIT_FAIL);
+}
+
+#ifdef MSVC
+
+NORET void __security_error_handler(void) {
+ log_output(LOG_FATAL, "Stack smashing detected in engine code");
+ rune_abort();
+}
+
+#else
+
+NORET void __stack_chk_fail(void) {
+ log_output(LOG_FATAL, "Stack smashing detected in engine code");
+ rune_abort();
+}
+
+#endif
diff --git a/engine/core/alloc.c b/engine/core/alloc.c
new file mode 100644
index 0000000..b75f1d3
--- /dev/null
+++ b/engine/core/alloc.c
@@ -0,0 +1,178 @@
+/*
+ * Rune Game Engine
+ * Copyright 2024 Danny Holman <dholman@gymli.org>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include <rune/core/alloc.h>
+#include <rune/core/logging.h>
+#include <rune/core/profiling.h>
+#include <stdlib.h>
+#include <string.h>
+
+// TODO: implement block coalescing so we can reuse freed blocks
+
+#define DEADBLOCK ((void*)0xDEADBEEF)
+
+static mem_block_t first_block;
+
+static mem_block_t* _find_free_block(size_t sz) {
+ list_head_t *temp = &first_block.list;
+ mem_block_t *block;
+ while (temp != NULL) {
+ block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list));
+ if (block->sz == sz && block->free == 1)
+ return block;
+ temp = temp->next;
+ }
+ return NULL;
+}
+
+static mem_block_t* _find_block(void *ptr) {
+ list_head_t *temp = &first_block.list;
+ mem_block_t *block;
+ while (temp != NULL) {
+ block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list));
+ if (block->ptr == ptr)
+ return block;
+ temp = temp->next;
+ }
+ return NULL;
+}
+
+static mem_block_t* _alloc_block(size_t sz) {
+ RUNE_PROFILE_SCOPE("Block allocation");
+ if (first_block.ptr == NULL) {
+ first_block.ptr = DEADBLOCK;
+ first_block.sz = 0;
+ }
+
+ mem_block_t *ret = _find_free_block(sz);
+ if (ret != NULL) {
+ ret->free = 0;
+ RUNE_PROFILE_END();
+ return ret->ptr;
+ }
+
+ ret = malloc(sizeof(mem_block_t));
+ if (ret == NULL) {
+ RUNE_PROFILE_END();
+ log_output(LOG_ERROR, "Cannot allocate block of size %d", sz);
+ return NULL;
+ }
+
+ ret->ptr = malloc(sz);
+ ret->sz = sz;
+ ret->free = 0;
+ list_add(&ret->list, &first_block.list);
+ RUNE_PROFILE_END();
+ log_output(LOG_DEBUG, "Alloc'd block of size %d", sz);
+ return ret;
+}
+
+static void _free_block(mem_block_t *block, int hard) {
+ RUNE_PROFILE_SCOPE("Block free");
+ if (hard == 1) {
+ list_del(&block->list);
+ RUNE_PROFILE_END();
+ free(block);
+ log_output(LOG_DEBUG, "Freed block of size %d", block->sz);
+ return;
+ }
+ block->free = 1;
+}
+
+void* rune_alloc(size_t sz) {
+ if (sz == 0)
+ return NULL;
+
+ RUNE_PROFILE_SCOPE("Pool allocation");
+ mem_block_t *block = _find_free_block(sz);
+ if (block != NULL) {
+ block->free = 0;
+ RUNE_PROFILE_END();
+ return block->ptr;
+ }
+
+ block = _alloc_block(sz);
+ RUNE_PROFILE_END();
+ return block->ptr;
+}
+
+void* rune_calloc(size_t nmemb, size_t sz) {
+ if (sz == 0)
+ return NULL;
+
+ RUNE_PROFILE_SCOPE("Zero array pool allocation");
+ mem_block_t *block = _find_free_block(sz);
+ if (block != NULL) {
+ block->free = 0;
+ RUNE_PROFILE_END();
+ return block->ptr;
+ }
+
+ block = _alloc_block(sz);
+ memset(block->ptr, 0, sz);
+ RUNE_PROFILE_END();
+ return block->ptr;
+}
+
+void* rune_realloc(void *ptr, size_t sz) {
+ if (ptr == NULL || sz == 0)
+ return rune_alloc(sz);
+
+ RUNE_PROFILE_SCOPE("Pool reallocation");
+ mem_block_t *old = _find_block(ptr);
+ mem_block_t *new = _alloc_block(sz);
+ memcpy(new->ptr, old->ptr, old->sz);
+ old->free = 1;
+ RUNE_PROFILE_END();
+ return new->ptr;
+}
+
+void rune_free(void *ptr) {
+ if (ptr == NULL)
+ return;
+
+ RUNE_PROFILE_SCOPE("Pool free");
+ mem_block_t *block = _find_block(ptr);
+ if (block->free == 1) {
+ RUNE_PROFILE_END();
+ return;
+ }
+ block->free = 1;
+ RUNE_PROFILE_END();
+}
+
+void rune_free_all(void) {
+ RUNE_PROFILE_SCOPE("Pool free all");
+ list_head_t *temp = &first_block.list;
+ mem_block_t *block;
+ while (temp != NULL) {
+ block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list));
+ if (block->ptr == DEADBLOCK) {
+ temp = temp->next;
+ continue;
+ }
+
+ temp = temp->next;
+ if (block->ptr != NULL)
+ _free_block(block, 1);
+ }
+ RUNE_PROFILE_END();
+}
diff --git a/engine/core/callbacks.c b/engine/core/callbacks.c
new file mode 100644
index 0000000..ea7f50e
--- /dev/null
+++ b/engine/core/callbacks.c
@@ -0,0 +1,27 @@
+/*
+ * Rune Game Engine
+ * Copyright 2024 Danny Holman <dholman@gymli.org>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include <rune/core/callbacks.h>
+#include <rune/core/logging.h>
+
+void error_callback(int error, const char *desc) {
+ log_output(LOG_ERROR, "%d: %s\n", error, desc);
+}
diff --git a/engine/core/init.c b/engine/core/init.c
new file mode 100644
index 0000000..6fcd086
--- /dev/null
+++ b/engine/core/init.c
@@ -0,0 +1,48 @@
+/*
+ * Rune Game Engine
+ * Copyright 2024 Danny Holman <dholman@gymli.org>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include <rune/core/init.h>
+#include <rune/core/abort.h>
+#include <rune/core/alloc.h>
+#include <rune/core/config.h>
+#include <rune/core/logging.h>
+#include <rune/core/thread.h>
+#include <rune/core/mod.h>
+#include <rune/core/object.h>
+
+int rune_init(int argc, char* argv[]) {
+ log_output(LOG_INFO, "Started Rune Engine version %s", RUNE_VER);
+
+ rune_init_default_settings();
+ rune_init_thread_api();
+
+ rune_load_mods();
+ rune_init_mods();
+
+ return 0;
+}
+
+void rune_exit(void) {
+ log_output(LOG_INFO, "Engine shutdown requested");
+ rune_clear_objs();
+ rune_close_mods();
+ rune_free_all();
+}
diff --git a/engine/core/logging.c b/engine/core/logging.c
new file mode 100644
index 0000000..b7f77bd
--- /dev/null
+++ b/engine/core/logging.c
@@ -0,0 +1,114 @@
+/*
+ * Rune Game Engine
+ * Copyright 2024 Danny Holman <dholman@gymli.org>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include <rune/core/logging.h>
+#include <rune/core/config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define COLOR_NONE ""
+#define COLOR_DEFAULT "\033[0m"
+#define COLOR_RED "\033[31m"
+#define COLOR_GREEN "\033[32m"
+#define COLOR_YELLOW "\033[33m"
+#define COLOR_BLUE "\033[34m"
+
+#define LSTR_FATAL "[FATAL]"
+#define LSTR_ERROR "[ERROR]"
+#define LSTR_WARN "[WARNING]"
+#define LSTR_INFO "[INFO]"
+#define LSTR_DEBUG "[DEBUG]"
+
+static int debug_enabled = 0;
+static int color_enabled = 0;
+
+void log_output(int level, const char *fmt, ...) {
+ char out[4096];
+ memset(out, 0, sizeof(out));
+
+ debug_enabled = rune_get_log_debug();
+ color_enabled = rune_get_log_color();
+
+ va_list arg_ptr;
+ va_start(arg_ptr, fmt);
+ vsnprintf(out, 4096, fmt, arg_ptr);
+ va_end(arg_ptr);
+
+ char *lvl_str;
+ char *color = COLOR_NONE;
+ switch (level) {
+ case LOG_FATAL:
+ if (color_enabled == 1)
+ color = COLOR_RED;
+ lvl_str = LSTR_FATAL;
+ break;
+ case LOG_ERROR:
+ if (color_enabled == 1)
+ color = COLOR_RED;
+ lvl_str = LSTR_ERROR;
+ break;
+ case LOG_WARN:
+ if (color_enabled == 1)
+ color = COLOR_YELLOW;
+ lvl_str = LSTR_WARN;
+ break;
+ case LOG_INFO:
+ lvl_str = LSTR_INFO;
+ break;
+ case LOG_DEBUG:
+ if (color_enabled == 1)
+ color = COLOR_GREEN;
+ if (debug_enabled == 0)
+ return;
+ lvl_str = LSTR_DEBUG;
+ break;
+ }
+
+ if (color_enabled == 0) {
+ printf("%s %s\n", lvl_str, out);
+ } else {
+ printf("%s%s %s\n", color, lvl_str, out);
+ printf(COLOR_DEFAULT);
+ }
+}
+
+//#ifdef _WIN32
+//
+//#include <windows.h>
+//
+//void enable_log_color(void) {
+// HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
+// SetConsoleMode(handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+// color_enabled = 1;
+//}
+//
+//#else
+//
+//void enable_log_color(void) {
+// color_enabled = 1;
+//}
+//
+//#endif
+//
+//void disable_log_color(void) {
+// color_enabled = 0;
+//}
diff --git a/engine/core/mod.c b/engine/core/mod.c
new file mode 100644
index 0000000..3d9e710
--- /dev/null
+++ b/engine/core/mod.c
@@ -0,0 +1,90 @@
+/*
+ * Rune Game Engine
+ * Copyright 2024 Danny Holman <dholman@gymli.org>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include <rune/core/mod.h>
+#include <rune/core/logging.h>
+#include <rune/core/alloc.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <dlfcn.h>
+
+list_head_t *mods = NULL;
+
+void _load_mod(const char *filename) {
+ char mod_path[4096];
+ sprintf(mod_path, "mods/%s", filename);
+ void *handle = dlopen(mod_path, RTLD_LAZY);
+ if (handle == NULL)
+ log_output(LOG_ERROR, "Error loading mod %s: %s", filename, dlerror());
+}
+
+void rune_load_mods(void) {
+ DIR *dir = opendir("mods");
+ if (dir == NULL) {
+ log_output(LOG_INFO, "No mods folder found, skipping mod loading");
+ return;
+ }
+
+ struct dirent *mods_de;
+ while ((mods_de = readdir(dir)) != NULL) {
+ if (strstr(mods_de->d_name, ".so"))
+ _load_mod(mods_de->d_name);
+ }
+}
+
+void rune_init_mods(void) {
+ list_head_t *temp = mods;
+ struct mod *mod;
+ while (temp != NULL) {
+ mod = (struct mod*)((void*)temp - offsetof(struct mod, list));
+ (*mod->init_func)();
+ temp = temp->next;
+ }
+}
+
+void rune_close_mods(void) {
+ if (mods == NULL)
+ return;
+
+ list_head_t *temp = mods;
+ struct mod *mod;
+ while (temp != NULL) {
+ mod = (struct mod*)((void*)temp - offsetof(struct mod, list));
+ (*mod->exit_func)();
+ temp = temp->next;
+ rune_free(mod);
+ }
+}
+
+void rune_register_mod(const char *name, mod_func init_func, mod_func exit_func, mod_func update_func) {
+ struct mod *new = rune_alloc(sizeof(struct mod));
+ new->name = name;
+ new->init_func = init_func;
+ new->exit_func = exit_func;
+ new->update_func = update_func;
+ new->list.next = NULL;
+ new->list.prev = NULL;
+ if (mods == NULL)
+ mods = &new->list;
+ else
+ list_add(&new->list, mods);
+}
diff --git a/engine/core/thread.c b/engine/core/thread.c
new file mode 100644
index 0000000..3c5db8c
--- /dev/null
+++ b/engine/core/thread.c
@@ -0,0 +1,218 @@
+/*
+ * Rune Game Engine
+ * Copyright 2024 Danny Holman <dholman@gymli.org>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#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 list_head_t *threads = NULL;
+static list_head_t *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;
+};
+
+static struct thread* _find_thread_by_handle(void *handle) {
+ if (threads == NULL)
+ return NULL;
+
+ list_head_t *temp = threads;
+ 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;
+}
+
+static struct thread* _find_thread_by_id(int ID) {
+ if (threads == NULL)
+ return NULL;
+
+ list_head_t *temp = threads;
+ 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;
+}
+
+static struct mutex* _find_mutex_by_id(int ID) {
+ if (mutexes == NULL)
+ return NULL;
+
+ list_head_t *temp = mutexes;
+ 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;
+}
+
+
+static void _cleanup_pthread(void *arg) {
+ struct thread *thread = (struct thread*)arg;
+ if (&thread->list != threads)
+ list_del(&thread->list);
+ rune_free(thread->thread_handle);
+ rune_free(thread);
+}
+
+static 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) {
+ struct thread *start_thread = rune_alloc(sizeof(struct thread));
+ start_thread->ID = next_tid++;
+ start_thread->detached = 0;
+ start_thread->thread_handle = rune_alloc(sizeof(pthread_t));
+ *(pthread_t*)start_thread->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));
+ if (threads == NULL)
+ threads = &thread->list;
+ else
+ list_add(&thread->list, threads);
+
+ 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->list;
+ else
+ list_add(&mutex->list, mutexes);
+ return mutex->ID;
+}
+
+int rune_mutex_destroy(int ID) {
+ struct mutex *mutex = _find_mutex_by_id(ID);
+ rune_free(mutex->mutex_handle);
+ if (&mutex->list != mutexes)
+ 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);
+ }
+}