aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Romanko <me@blankhex.com>2025-03-02 23:18:23 +0300
committerMikhail Romanko <me@blankhex.com>2025-03-02 23:18:23 +0300
commitd403d41f2c54ca382d3a1be17491fdf94097c693 (patch)
tree9aa2dc87bca34ee2360f82e87bbc0ce09eff0a70
parent2ca6a3e316f356c0467f52f80d8588ee8ed76314 (diff)
downloadbhlib-d403d41f2c54ca382d3a1be17491fdf94097c693.tar.gz
Add initial implementation of threads/mutexes/semaphores/cvs/spinlocks
Added initial implementation (or wrapper) of the threading library. It's rather basic, but should work for most of the tasks. Unfortunately, spinlock implementation relies on GCC/Clang compiler built-ins (or in-worst-case-scenario on Win32 - InterlockExchange). In the future, I should revisit this code and fix/reimplement some stuff (or add support for Windows XP).
-rw-r--r--CMakeLists.txt83
-rw-r--r--include/BH/Thread.h302
-rw-r--r--src/Platform/Dummy/Condition.c53
-rw-r--r--src/Platform/Dummy/Mutex.c39
-rw-r--r--src/Platform/Dummy/Semaphore.c51
-rw-r--r--src/Platform/Dummy/Thread.c31
-rw-r--r--src/Platform/Dummy/Thread.h33
-rw-r--r--src/Platform/Dummy/Tss.c24
-rw-r--r--src/Platform/Posix/Condition.c74
-rw-r--r--src/Platform/Posix/Mutex.c57
-rw-r--r--src/Platform/Posix/Semaphore.c73
-rw-r--r--src/Platform/Posix/Thread.c98
-rw-r--r--src/Platform/Posix/Thread.h35
-rw-r--r--src/Platform/Posix/Tss.c97
-rw-r--r--src/Platform/Spinlock.c50
-rw-r--r--src/Platform/Win32/Condition.c58
-rw-r--r--src/Platform/Win32/Mutex.c51
-rw-r--r--src/Platform/Win32/Semaphore.c69
-rw-r--r--src/Platform/Win32/Thread.c90
-rw-r--r--src/Platform/Win32/Thread.h34
-rw-r--r--src/Platform/Win32/Tss.c99
21 files changed, 1492 insertions, 9 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eb8299a..ba98e42 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,6 +51,7 @@ set(ENABLE_TESTING ON CACHE BOOL "Enable unit-testing")
set(ENABLE_COVERAGE OFF CACHE BOOL "Enable coverage")
set(ENABLE_EXAMPLES ON CACHE BOOL "Enable building examples")
set(ENABLE_LTO ON CACHE BOOL "Enable LTO support")
+set(ENABLE_MT ON CACHE BOOL "Enable multithreading support")
# Enable IPO/LTO
if(ENABLE_LTO)
@@ -102,6 +103,57 @@ set(BH_HEADER
include/BH/Math.h
include/BH/Queue.h
include/BH/Util.h
+ include/BH/Thread.h
+)
+
+set(BH_SOURCE_DUMMY
+ src/Platform/Dummy/File.c
+)
+
+set(BH_SOURCE_WIN32
+ src/Platform/Win32/File.c
+)
+
+set(BH_SOURCE_POSIX
+ src/Platform/Posix/File.c
+)
+
+set(BH_SOURCE_WIN32_MT
+ src/Platform/Win32/Condition.c
+ src/Platform/Win32/Mutex.c
+ src/Platform/Win32/Semaphore.c
+ src/Platform/Win32/Thread.c
+ src/Platform/Win32/Tss.c
+ src/Platform/Spinlock.c
+)
+
+set(BH_HEADER_WIN32_MT
+ src/Platform/Win32/Thread.h
+)
+
+set(BH_SOURCE_POSIX_MT
+ src/Platform/Posix/Condition.c
+ src/Platform/Posix/Mutex.c
+ src/Platform/Posix/Semaphore.c
+ src/Platform/Posix/Thread.c
+ src/Platform/Posix/Tss.c
+ src/Platform/Spinlock.c
+)
+
+set(BH_HEADER_POSIX_MT
+ src/Platform/Posix/Thread.h
+)
+
+set(BH_SOURCE_DUMMY_MT
+ src/Platform/Dummy/Condition.c
+ src/Platform/Dummy/Mutex.c
+ src/Platform/Dummy/Semaphore.c
+ src/Platform/Dummy/Thread.c
+ src/Platform/Dummy/Tss.c
+)
+
+set(BH_HEADER_DUMMY_MT
+ src/Platform/Dummy/Thread.h
)
set(BH_INCLUDE_DIRS
@@ -114,23 +166,36 @@ if(WIN32)
message(STATUS "Platform - Win32")
# Add platform dependent files
- list(APPEND BH_SOURCE
- src/Platform/Win32/File.c
- )
+ list(APPEND BH_SOURCE ${BH_SOURCE_WIN32})
+
+ # Add platform dependent multithreading support
+ if(ENABLE_MT)
+ list(APPEND BH_SOURCE ${BH_SOURCE_WIN32_MT})
+ list(APPEND BH_HEADER ${BH_HEADER_WIN32_MT})
+ else()
+ list(APPEND BH_SOURCE ${BH_SOURCE_DUMMY_MT})
+ list(APPEND BH_HEADER ${BH_HEADER_DUMMY_MT})
+ endif()
elseif(UNIX)
message(STATUS "Platform: Unix (Linux/BSD/MacOS X)")
# Add platform dependent files
- list(APPEND BH_SOURCE
- src/Platform/Posix/File.c
- )
+ list(APPEND BH_SOURCE BH_SOURCE_POSIX)
+
+ # Add platform dependent multithreading support
+ if(ENABLE_MT)
+ list(APPEND BH_SOURCE ${BH_SOURCE_POSIX_MT})
+ list(APPEND BH_HEADER ${BH_HEADER_POSIX_MT})
+ else()
+ list(APPEND BH_SOURCE ${BH_SOURCE_POSIX_MT})
+ list(APPEND BH_HEADER ${BH_HEADER_POSIX_MT})
+ endif()
else()
message(STATUS "Platform: Unknown")
# Add platform dependent files
- list(APPEND BH_SOURCE
- src/Platform/Dummy/File.c
- )
+ list(APPEND BH_SOURCE ${BH_SOURCE_DUMMY} ${BH_SOURCE_DUMMY_MT})
+ list(APPEND BH_HEADER ${BH_HEADER_DUMMY_MT})
endif()
# Library
diff --git a/include/BH/Thread.h b/include/BH/Thread.h
new file mode 100644
index 0000000..794abc5
--- /dev/null
+++ b/include/BH/Thread.h
@@ -0,0 +1,302 @@
+#ifndef BH_THREAD_H
+#define BH_THREAD_H
+
+
+#include "Common.h"
+
+
+#define BH_MAX_TSS 128
+
+
+typedef struct BH_Lock BH_Lock;
+typedef struct BH_Thread BH_Thread;
+typedef struct BH_Mutex BH_Mutex;
+typedef struct BH_Semaphore BH_Semaphore;
+typedef struct BH_Condition BH_Condition;
+typedef int (*BH_ThreadCallback)(void *);
+
+
+/**
+ * Creates new thread with specified \a stack size, thread \a callback
+ * function and \a data.
+ *
+ * \param stack Stack size
+ * \param callback Callback function
+ * \param data User data
+ *
+ * \return On success, returns thread pointer.
+ * \return On failure, returns NULL pointer.
+ */
+BH_Thread *BH_ThreadNew(size_t stack,
+ BH_ThreadCallback callback,
+ void *data);
+
+
+/**
+ * Joins the specified \a thread.
+ *
+ * \param thread Thread
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_ThreadJoin(BH_Thread *thread);
+
+
+/**
+ * Detaches the specified \a thread.
+ *
+ * \param thread Thread
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_ThreadDetach(BH_Thread *thread);
+
+
+/**
+ * Creates new mutex.
+ *
+ * \return On success, returns mutex pointer.
+ * \return On failure, returns NULL pointer.
+ */
+BH_Mutex *BH_MutexNew(void);
+
+
+/**
+ * Frees the \a mutex.
+ *
+ * \param mutex Mutex
+ */
+void BH_MutexFree(BH_Mutex *mutex);
+
+
+/**
+ * Locks the \a mutex.
+ *
+ * \param mutex Mutex
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_MutexLock(BH_Mutex *mutex);
+
+
+/**
+ * Unlocks the \a mutex.
+ *
+ * \param mutex Mutex
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_MutexUnlock(BH_Mutex *mutex);
+
+
+/**
+ * Tries to lock the \a mutex.
+ *
+ * \param mutex Mutex
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_MutexLockTry(BH_Mutex *mutex);
+
+
+/**
+ * Creates new semaphore.
+ *
+ * \param value Semaphore value
+ *
+ * \return On success, returns thread pointer.
+ * \return On failure, returns NULL pointer.
+ */
+BH_Semaphore *BH_SemaphoreNew(int value);
+
+
+/**
+ * Frees the \a semaphore.
+ *
+ * \param semaphore Semaphore
+ */
+void BH_SemaphoreFree(BH_Semaphore *semaphore);
+
+
+/**
+ * Posts/increments the \a semaphore.
+ *
+ * \param semaphore Semaphore
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_SemaphorePost(BH_Semaphore *semaphore);
+
+
+/**
+ * Waits/decrements the \a semaphore.
+ *
+ * \param semaphore Semaphore
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_SemaphoreWait(BH_Semaphore *semaphore);
+
+
+/**
+ * Tries to wait/decrement the \a semaphore.
+ *
+ * \param semaphore Semaphore
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_SemaphoreWaitTry(BH_Semaphore *semaphore);
+
+
+/**
+ * Waits/decrements the \a semaphore with \a timeout.
+ *
+ * If timeout occures, return code will be BH_TIMEOUT.
+ *
+ * \param semaphore Semaphore
+ * \param timeout Timeout in milliseconds
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_SemaphoreWaitFor(BH_Semaphore *semaphore,
+ uint32_t timeout);
+
+
+/**
+ * Creates new condition variable.
+ *
+ * \return On success, returns condition variable pointer.
+ * \return On failure, returns NULL pointer.
+ */
+BH_Condition *BH_ConditionNew(void);
+
+
+/**
+ * Frees the \a condition variable.
+ *
+ * \param condition Condition
+ */
+void BH_ConditionFree(BH_Condition *condition);
+
+
+/**
+ * Unlocks the \a mutex and waits for the \a condition variable.
+ *
+ * \param condition Condition variable
+ * \param mutex Mutex
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_ConditionWait(BH_Condition *condition,
+ BH_Mutex *mutex);
+
+
+/**
+ * Unlocks the \a mutex and waits for the \a condition variable with \a timeout.
+ *
+ * If timeout occures, return code will be BH_TIMEOUT.
+ *
+ * \param condition Condition variable
+ * \param mutex Mutex
+ * \param timeout Timeout in milliseconds
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_ConditionWaitFor(BH_Condition *condition,
+ BH_Mutex *mutex,
+ uint32_t timeout);
+
+
+/**
+ * Signals the \a condition variable.
+ *
+ * \param condition Condition variable
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_ConditionSignal(BH_Condition *condition);
+
+
+/**
+ * Broadcasts the \a condition variable.
+ *
+ * \param condition Condition variable
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_ConditionBroadcast(BH_Condition *condition);
+
+
+/**
+ * Locks the \a spinlock.
+ *
+ * \param lock Spinlock
+ */
+void BH_SpinlockLock(int *lock);
+
+
+
+/**
+ * Tries to lock the \a spinlock.
+ *
+ * \param lock Spinlock
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int BH_SpinlockLockTry(int *lock);
+
+
+/**
+ * Unlocks the \a spinlock.
+ *
+ * \param lock Spinlock
+ */
+void BH_SpinlockUnlock(int *lock);
+
+
+/**
+ * Create new TSS/TLS slot index and destruction \a callback.
+ *
+ * \param callback Destruction callback
+ *
+ * \return On success, returns slot index.
+ * \return On failure, returns error code.
+ */
+int BH_TssCreate(BH_GenericCallback callback);
+
+
+/**
+ * Reads the value associated with the TSS \a index.
+ *
+ * \param index TSS index
+ *
+ * \return Index value
+ */
+void *BH_TssRead(int index);
+
+
+/**
+ * Writes the \a value to the TSS \a index.
+ *
+ * \param index TSS index to
+ * \param value Value
+ */
+void BH_TssWrite(int index,
+ void *value);
+
+
+#endif /* BH_THREAD_H */
diff --git a/src/Platform/Dummy/Condition.c b/src/Platform/Dummy/Condition.c
new file mode 100644
index 0000000..64d7856
--- /dev/null
+++ b/src/Platform/Dummy/Condition.c
@@ -0,0 +1,53 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+
+
+BH_Condition *BH_ConditionNew(void)
+{
+ return NULL;
+}
+
+
+void BH_ConditionFree(BH_Condition *condition)
+{
+ BH_UNUSED(condition);
+}
+
+
+int BH_ConditionWait(BH_Condition *condition,
+ BH_Mutex *mutex)
+{
+ BH_UNUSED(condition);
+ BH_UNUSED(mutex);
+
+ return BH_NOIMPL;
+}
+
+
+int BH_ConditionWaitFor(BH_Condition *condition,
+ BH_Mutex *mutex,
+ uint32_t timeout)
+{
+ BH_UNUSED(condition);
+ BH_UNUSED(mutex);
+ BH_UNUSED(timeout);
+
+ return BH_NOIMPL;
+}
+
+
+int BH_ConditionSignal(BH_Condition *condition)
+{
+ BH_UNUSED(condition);
+
+ return BH_NOIMPL;
+}
+
+
+int BH_ConditionBroadcast(BH_Condition *condition)
+{
+ BH_UNUSED(condition);
+
+ return BH_NOIMPL;
+}
diff --git a/src/Platform/Dummy/Mutex.c b/src/Platform/Dummy/Mutex.c
new file mode 100644
index 0000000..8024fa4
--- /dev/null
+++ b/src/Platform/Dummy/Mutex.c
@@ -0,0 +1,39 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+
+
+BH_Mutex *BH_MutexNew(void)
+{
+ return NULL;
+}
+
+
+void BH_MutexFree(BH_Mutex *mutex)
+{
+ BH_UNUSED(mutex);
+}
+
+
+int BH_MutexLock(BH_Mutex *mutex)
+{
+ BH_UNUSED(mutex);
+
+ return BH_NOIMPL;
+}
+
+
+int BH_MutexUnlock(BH_Mutex *mutex)
+{
+ BH_UNUSED(mutex);
+
+ return BH_NOIMPL;
+}
+
+
+int BH_MutexLockTry(BH_Mutex *mutex)
+{
+ BH_UNUSED(mutex);
+
+ return BH_NOIMPL;
+}
diff --git a/src/Platform/Dummy/Semaphore.c b/src/Platform/Dummy/Semaphore.c
new file mode 100644
index 0000000..72d0bc4
--- /dev/null
+++ b/src/Platform/Dummy/Semaphore.c
@@ -0,0 +1,51 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+
+
+BH_Semaphore *BH_SemaphoreNew(int value)
+{
+ BH_UNUSED(value);
+
+ return NULL;
+}
+
+
+void BH_SemaphoreFree(BH_Semaphore *semaphore)
+{
+ BH_UNUSED(semaphore);
+}
+
+
+int BH_SemaphorePost(BH_Semaphore *semaphore)
+{
+ BH_UNUSED(semaphore);
+
+ return BH_NOIMPL;
+}
+
+
+int BH_SemaphoreWait(BH_Semaphore *semaphore)
+{
+ BH_UNUSED(semaphore);
+
+ return BH_NOIMPL;
+}
+
+
+int BH_SemaphoreWaitTry(BH_Semaphore *semaphore)
+{
+ BH_UNUSED(semaphore);
+
+ return BH_NOIMPL;
+}
+
+
+int BH_SemaphoreWaitFor(BH_Semaphore *semaphore,
+ uint32_t timeout)
+{
+ BH_UNUSED(semaphore);
+ BH_UNUSED(timeout);
+
+ return BH_NOIMPL;
+}
diff --git a/src/Platform/Dummy/Thread.c b/src/Platform/Dummy/Thread.c
new file mode 100644
index 0000000..a36a780
--- /dev/null
+++ b/src/Platform/Dummy/Thread.c
@@ -0,0 +1,31 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+
+
+BH_Thread *BH_ThreadNew(size_t stack,
+ BH_ThreadCallback callback,
+ void *data)
+{
+ BH_UNUSED(stack);
+ BH_UNUSED(callback);
+ BH_UNUSED(data);
+
+ return NULL;
+}
+
+
+int BH_ThreadJoin(BH_Thread *thread)
+{
+ BH_UNUSED(thread);
+
+ return BH_NOIMPL;
+}
+
+
+int BH_ThreadDetach(BH_Thread *thread)
+{
+ BH_UNUSED(thread);
+
+ return BH_NOIMPL;
+}
diff --git a/src/Platform/Dummy/Thread.h b/src/Platform/Dummy/Thread.h
new file mode 100644
index 0000000..30ec213
--- /dev/null
+++ b/src/Platform/Dummy/Thread.h
@@ -0,0 +1,33 @@
+#ifndef BH_PLATFORM_DUMMY_THREAD_H
+#define BH_PLATFORM_DUMMY_THREAD_H
+
+
+struct BH_Condition
+{
+ int implement;
+ int me;
+};
+
+
+struct BH_Mutex
+{
+ int implement;
+ int me;
+};
+
+
+struct BH_Semaphore
+{
+ int implement;
+ int me;
+};
+
+
+struct BH_Thread
+{
+ int implement;
+ int me;
+};
+
+
+#endif /* BH_PLATFORM_DUMMY_THREAD_H */
diff --git a/src/Platform/Dummy/Tss.c b/src/Platform/Dummy/Tss.c
new file mode 100644
index 0000000..a6582c9
--- /dev/null
+++ b/src/Platform/Dummy/Tss.c
@@ -0,0 +1,24 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+
+
+int BH_TssCreate(BH_GenericCallback callback)
+{
+
+ BH_UNUSED(callback);
+}
+
+
+void *BH_TssRead(int index)
+{
+ return NULL;
+}
+
+
+void BH_TssWrite(int index,
+ void *value)
+{
+ BH_UNUSED(index);
+ BH_UNUSED(value);
+}
diff --git a/src/Platform/Posix/Condition.c b/src/Platform/Posix/Condition.c
new file mode 100644
index 0000000..10ea1f4
--- /dev/null
+++ b/src/Platform/Posix/Condition.c
@@ -0,0 +1,74 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+#include <stdlib.h>
+#include <errno.h>
+
+
+BH_Condition *BH_ConditionNew(void)
+{
+ BH_Condition *condition;
+
+ /* Allocate space for mutex and initialize it */
+ condition = malloc(sizeof(BH_Condition));
+ if (condition && pthread_cond_init(&condition->handle, NULL))
+ {
+ free(condition);
+ return NULL;
+ }
+
+ return condition;
+}
+
+
+void BH_ConditionFree(BH_Condition *condition)
+{
+ pthread_cond_destroy(&condition->handle);
+ free(condition);
+}
+
+
+int BH_ConditionWait(BH_Condition *condition,
+ BH_Mutex *mutex)
+{
+ if (pthread_cond_wait(&condition->handle, &mutex->handle))
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+int BH_ConditionWaitFor(BH_Condition *condition,
+ BH_Mutex *mutex,
+ uint32_t timeout)
+{
+ struct timespec ts;
+
+ ts.tv_sec = timeout / 1000;
+ ts.tv_nsec = (timeout - ts.tv_sec * 1000) * 1000000;
+
+ switch (pthread_cond_timedwait(&condition->handle, &mutex->handle, &ts))
+ {
+ case 0: return BH_OK;
+ case ETIMEDOUT: return BH_TIMEOUT;
+ default: return BH_ERROR;
+ }
+}
+
+
+int BH_ConditionSignal(BH_Condition *condition)
+{
+ if (pthread_cond_signal(&condition->handle))
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+int BH_ConditionBroadcast(BH_Condition *condition)
+{
+ if (pthread_cond_broadcast(&condition->handle))
+ return BH_ERROR;
+
+ return BH_OK;
+}
diff --git a/src/Platform/Posix/Mutex.c b/src/Platform/Posix/Mutex.c
new file mode 100644
index 0000000..a903d28
--- /dev/null
+++ b/src/Platform/Posix/Mutex.c
@@ -0,0 +1,57 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+#include <stdlib.h>
+#include <errno.h>
+
+
+BH_Mutex *BH_MutexNew(void)
+{
+ BH_Mutex *mutex;
+
+ /* Allocate space for mutex and initialize it */
+ mutex = malloc(sizeof(BH_Mutex));
+ if (mutex && pthread_mutex_init(&mutex->handle, NULL))
+ {
+ free(mutex);
+ return NULL;
+ }
+
+ return mutex;
+}
+
+
+void BH_MutexFree(BH_Mutex *mutex)
+{
+ pthread_mutex_destroy(&mutex->handle);
+ free(mutex);
+}
+
+
+int BH_MutexLock(BH_Mutex *mutex)
+{
+ if (pthread_mutex_lock(&mutex->handle))
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+int BH_MutexUnlock(BH_Mutex *mutex)
+{
+ if (pthread_mutex_unlock(&mutex->handle))
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+int BH_MutexLockTry(BH_Mutex *mutex)
+{
+ switch (pthread_mutex_trylock(&mutex->handle))
+ {
+ case 0: return BH_OK;
+ case EBUSY: return BH_TIMEOUT;
+ default: return BH_ERROR;
+ }
+}
diff --git a/src/Platform/Posix/Semaphore.c b/src/Platform/Posix/Semaphore.c
new file mode 100644
index 0000000..630475f
--- /dev/null
+++ b/src/Platform/Posix/Semaphore.c
@@ -0,0 +1,73 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+
+
+BH_Semaphore *BH_SemaphoreNew(int value)
+{
+ BH_Semaphore *semaphore;
+
+ /* Allocate space for mutex and initialize it */
+ semaphore = malloc(sizeof(BH_Semaphore));
+ if (semaphore && sem_init(&semaphore->handle, 0, value))
+ {
+ free(semaphore);
+ return NULL;
+ }
+
+ return semaphore;
+}
+
+
+void BH_SemaphoreFree(BH_Semaphore *semaphore)
+{
+ sem_destroy(&semaphore->handle);
+ free(semaphore);
+}
+
+
+int BH_SemaphorePost(BH_Semaphore *semaphore)
+{
+ if (sem_post(&semaphore->handle))
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+int BH_SemaphoreWait(BH_Semaphore *semaphore)
+{
+ if (sem_wait(&semaphore->handle))
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+int BH_SemaphoreWaitTry(BH_Semaphore *semaphore)
+{
+ if (sem_trywait(&semaphore->handle))
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+int BH_SemaphoreWaitFor(BH_Semaphore *semaphore,
+ uint32_t timeout)
+{
+ struct timespec ts;
+
+ ts.tv_sec = timeout / 1000;
+ ts.tv_nsec = (timeout - ts.tv_sec * 1000) * 1000000;
+
+ switch (sem_timedwait(&semaphore->handle, &ts))
+ {
+ case 0: return BH_OK;
+ case ETIMEDOUT: return BH_TIMEOUT;
+ default: return BH_ERROR;
+ }
+}
diff --git a/src/Platform/Posix/Thread.c b/src/Platform/Posix/Thread.c
new file mode 100644
index 0000000..1771eb6
--- /dev/null
+++ b/src/Platform/Posix/Thread.c
@@ -0,0 +1,98 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+#include <limits.h>
+#include <stdlib.h>
+
+
+struct BH_ThreadContext
+{
+ BH_ThreadCallback callback;
+ void *data;
+};
+
+
+static void *BH_ThreadRun(void *context)
+{
+ BH_ThreadCallback callback;
+ void *data, *result;
+
+ callback = ((struct BH_ThreadContext *)context)->callback;
+ data = ((struct BH_ThreadContext *)context)->data;
+ free(context);
+
+ callback(data);
+ BH_TssCleanup();
+
+ pthread_exit(0);
+}
+
+
+static int BH_ThreadInit(BH_Thread *thread,
+ size_t stack,
+ BH_ThreadCallback callback,
+ void *data)
+{
+ struct BH_ThreadContext *context;
+ pthread_attr_t attributes;
+ int result;
+
+ context = malloc(sizeof(*context));
+ if (!context)
+ return BH_ERROR;
+ context->callback = callback;
+ context->data = data;
+
+ /* Create thread with specified stack size */
+ pthread_attr_init(&attributes);
+
+ if (!stack)
+ result = pthread_create(&thread->handle, NULL, BH_ThreadRun, context);
+ else
+ {
+ if (stack < PTHREAD_STACK_MIN)
+ stack = PTHREAD_STACK_MIN;
+ pthread_attr_setstacksize(&attributes, stack);
+ result = pthread_create(&thread->handle, &attributes, BH_ThreadRun, context);
+ }
+
+ pthread_attr_destroy(&attributes);
+ return result;
+}
+
+
+BH_Thread *BH_ThreadNew(size_t stack,
+ BH_ThreadCallback callback,
+ void *data)
+{
+ BH_Thread *thread;
+
+ thread = malloc(sizeof(BH_Thread));
+ if (thread && BH_ThreadInit(thread, stack, callback, data))
+ {
+ free(thread);
+ return NULL;
+ }
+
+ return thread;
+}
+
+
+int BH_ThreadJoin(BH_Thread *thread)
+{
+ if (pthread_join(thread->handle, NULL))
+ return BH_ERROR;
+
+ free(thread);
+ return BH_OK;
+}
+
+
+int BH_ThreadDetach(BH_Thread *thread)
+{
+ if (pthread_detach(thread->handle))
+ return BH_ERROR;
+
+ free(thread);
+ return BH_OK;
+}
diff --git a/src/Platform/Posix/Thread.h b/src/Platform/Posix/Thread.h
new file mode 100644
index 0000000..69c6c0f
--- /dev/null
+++ b/src/Platform/Posix/Thread.h
@@ -0,0 +1,35 @@
+#ifndef BH_PLATFORM_POSIX_THREAD_H
+#define BH_PLATFORM_POSIX_THREAD_H
+
+
+#include <pthread.h>
+#include <semaphore.h>
+
+
+struct BH_Condition
+{
+ pthread_cond_t handle;
+};
+
+
+struct BH_Mutex
+{
+ pthread_mutex_t handle;
+};
+
+
+struct BH_Semaphore
+{
+ sem_t handle;
+};
+
+
+struct BH_Thread
+{
+ pthread_t handle;
+};
+
+
+void BH_TssCleanup(void);
+
+#endif /* BH_PLATFORM_POSIX_THREAD_H */
diff --git a/src/Platform/Posix/Tss.c b/src/Platform/Posix/Tss.c
new file mode 100644
index 0000000..a16d653
--- /dev/null
+++ b/src/Platform/Posix/Tss.c
@@ -0,0 +1,97 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+static int tssCleanupLock = 0;
+static int tssCleanupSize = 0;
+static BH_GenericCallback tssCleanupData[BH_MAX_TSS];
+
+static int tssLock = 0;
+static int tssReady = 0;
+static pthread_key_t tssKey;
+
+
+static void BH_TssKeyCleanup(void *data)
+{
+ int i;
+
+ /* Lock cleanup table and call cleanups */
+ BH_SpinlockLock(&tssCleanupLock);
+ for (i = 0; i < tssCleanupSize; i++)
+ tssCleanupData[i](((void **)data)[i]);
+ BH_SpinlockUnlock(&tssCleanupLock);
+
+ free(data);
+}
+
+
+static void **BH_TssDataFetch(void)
+{
+ void **result;
+
+ /* Get or setup TSS data */
+ BH_SpinlockLock(&tssLock);
+ if (!tssReady)
+ {
+ if (pthread_key_create(&tssKey, BH_TssKeyCleanup))
+ abort();
+ tssReady = 1;
+ }
+ BH_SpinlockUnlock(&tssLock);
+
+ /* Get or setup TSS data */
+ result = pthread_getspecific(tssKey);
+ if (!result)
+ {
+ result = malloc(sizeof(void *) * BH_MAX_TSS);
+ if (!result)
+ abort();
+
+ memset(result, 0, sizeof(sizeof(void *) * BH_MAX_TSS));
+ if (pthread_setspecific(tssKey, result))
+ abort();
+ }
+
+ return result;
+}
+
+
+void BH_TssCleanup(void)
+{
+ BH_TssKeyCleanup(BH_TssDataFetch());
+}
+
+
+int BH_TssCreate(BH_GenericCallback callback)
+{
+ int result;
+
+ /* Lock info and set up cleanup function */
+ BH_SpinlockLock(&tssCleanupLock);
+ if (tssCleanupSize < BH_MAX_TSS)
+ {
+ tssCleanupData[tssCleanupSize] = callback;
+ result = tssCleanupSize++;
+ }
+ else
+ result = BH_ERROR;
+ BH_SpinlockUnlock(&tssCleanupLock);
+
+ return result;
+}
+
+
+void *BH_TssRead(int index)
+{
+ return BH_TssDataFetch()[index];
+}
+
+
+void BH_TssWrite(int index,
+ void *value)
+{
+ BH_TssDataFetch()[index] = value;
+}
diff --git a/src/Platform/Spinlock.c b/src/Platform/Spinlock.c
new file mode 100644
index 0000000..3583500
--- /dev/null
+++ b/src/Platform/Spinlock.c
@@ -0,0 +1,50 @@
+#include <BH/Thread.h>
+
+
+#if defined(__clang__) || defined(__GNUC__)
+/* Using built-ins */
+#elif defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+
+void BH_SpinlockLock(int *lock)
+{
+#if defined(__clang__) || defined(__GNUC__)
+ while (__sync_lock_test_and_set(lock, 1));
+#elif defined(_WIN32)
+ while (InterlockedExchange(lock, 1));
+#else
+ #error "Spinlocks are not supported"
+#endif
+}
+
+
+int BH_SpinlockLockTry(int *lock)
+{
+#if defined(__clang__) || defined(__GNUC__)
+ if (__sync_lock_test_and_set(lock, 1))
+ return BH_ERROR;
+ return BH_OK;
+#elif defined(_WIN32)
+ if (InterlockedExchange(lock, 1))
+ return BH_ERROR;
+ return BH_OK;
+#else
+ #error "Spinlocks are not supported"
+ return BH_NOIMPL;
+#endif
+}
+
+
+void BH_SpinlockUnlock(int *lock)
+{
+#if defined(__clang__) || defined(__GNUC__)
+ __sync_lock_release(lock);
+#elif defined(_WIN32)
+ InterlockedExchange(lock, 0);
+#else
+ #error "Spinlocks are not supported"
+#endif
+}
diff --git a/src/Platform/Win32/Condition.c b/src/Platform/Win32/Condition.c
new file mode 100644
index 0000000..1d2bcac
--- /dev/null
+++ b/src/Platform/Win32/Condition.c
@@ -0,0 +1,58 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+
+
+BH_Condition *BH_ConditionNew(void)
+{
+ BH_Condition *condition;
+
+ /* Allocate space for mutex and initialize it */
+ condition = malloc(sizeof(BH_Condition));
+ if (condition)
+ {
+ InitializeConditionVariable(&condition->handle);
+ }
+
+ return condition;
+}
+
+
+void BH_ConditionFree(BH_Condition *condition)
+{
+ BH_UNUSED(condition);
+}
+
+
+int BH_ConditionWait(BH_Condition *condition,
+ BH_Mutex *mutex)
+{
+ return BH_ConditionWaitFor(condition, mutex, INFINITE);
+}
+
+
+int BH_ConditionWaitFor(BH_Condition *condition,
+ BH_Mutex *mutex,
+ uint32_t timeout)
+{
+ switch (SleepConditionVariableCS(&condition->handle, &mutex->handle, timeout))
+ {
+ case 0: return BH_ERROR;
+ case ERROR_TIMEOUT: return BH_TIMEOUT;
+ default: return BH_OK;
+ }
+}
+
+
+int BH_ConditionSignal(BH_Condition *condition)
+{
+ WakeConditionVariable(&condition->handle);
+ return BH_OK;
+}
+
+
+int BH_ConditionBroadcast(BH_Condition *condition)
+{
+ WakeAllConditionVariable(&condition->handle);
+ return BH_OK;
+}
diff --git a/src/Platform/Win32/Mutex.c b/src/Platform/Win32/Mutex.c
new file mode 100644
index 0000000..f34aac1
--- /dev/null
+++ b/src/Platform/Win32/Mutex.c
@@ -0,0 +1,51 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+
+
+BH_Mutex *BH_MutexNew(void)
+{
+ BH_Mutex *mutex;
+
+ /* Allocate space for mutex and initialize it */
+ mutex = malloc(sizeof(BH_Mutex));
+ if (mutex && !InitializeCriticalSectionAndSpinCount(&mutex->handle, 0x400))
+ {
+ free(mutex);
+ return NULL;
+ }
+
+ return mutex;
+}
+
+
+void BH_MutexFree(BH_Mutex *mutex)
+{
+ DeleteCriticalSection(&mutex->handle);
+ free(mutex);
+}
+
+
+int BH_MutexLock(BH_Mutex *mutex)
+{
+ EnterCriticalSection(&mutex->handle);
+
+ return BH_OK;
+}
+
+
+int BH_MutexUnlock(BH_Mutex *mutex)
+{
+ LeaveCriticalSection(&mutex->handle);
+
+ return BH_OK;
+}
+
+
+int BH_MutexLockTry(BH_Mutex *mutex)
+{
+ if (!TryEnterCriticalSection(&mutex->handle))
+ return BH_ERROR;
+
+ return BH_OK;
+}
diff --git a/src/Platform/Win32/Semaphore.c b/src/Platform/Win32/Semaphore.c
new file mode 100644
index 0000000..39fb868
--- /dev/null
+++ b/src/Platform/Win32/Semaphore.c
@@ -0,0 +1,69 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+
+
+BH_Semaphore *BH_SemaphoreNew(int value)
+{
+ BH_Semaphore *semaphore;
+
+ /* Allocate space for mutex and initialize it */
+ semaphore = malloc(sizeof(BH_Semaphore));
+ if (semaphore)
+ {
+ semaphore->handle = CreateSemaphore(NULL, value, 0x7FFF, NULL);
+ if (!semaphore->handle)
+ {
+ free(semaphore);
+ return NULL;
+ }
+ }
+
+ return semaphore;
+}
+
+
+void BH_SemaphoreFree(BH_Semaphore *semaphore)
+{
+ CloseHandle(semaphore->handle);
+ free(semaphore);
+}
+
+
+int BH_SemaphorePost(BH_Semaphore *semaphore)
+{
+ if (!ReleaseSemaphore(semaphore->handle, 1, NULL))
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+int BH_SemaphoreWait(BH_Semaphore *semaphore)
+{
+ if (WaitForSingleObject(semaphore->handle, INFINITE))
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+int BH_SemaphoreWaitTry(BH_Semaphore *semaphore)
+{
+ if (WaitForSingleObject(semaphore->handle, 0))
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+int BH_SemaphoreWaitFor(BH_Semaphore *semaphore,
+ uint32_t timeout)
+{
+ switch (WaitForSingleObject(semaphore->handle, timeout))
+ {
+ case 0: return BH_OK;
+ case WAIT_TIMEOUT: return BH_TIMEOUT;
+ default: return BH_ERROR;
+ }
+}
diff --git a/src/Platform/Win32/Thread.c b/src/Platform/Win32/Thread.c
new file mode 100644
index 0000000..63de498
--- /dev/null
+++ b/src/Platform/Win32/Thread.c
@@ -0,0 +1,90 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+#include <process.h>
+
+
+struct BH_ThreadContext
+{
+ BH_ThreadCallback callback;
+ void *data;
+};
+
+
+static unsigned __stdcall BH_ThreadRun(void *context)
+{
+
+ BH_ThreadCallback callback;
+ void *data;
+
+ callback = ((struct BH_ThreadContext *)context)->callback;
+ data = ((struct BH_ThreadContext *)context)->data;
+ free(context);
+
+ callback(data);
+ BH_TssCleanup();
+
+ _endthreadex(0);
+}
+
+
+static int BH_ThreadInit(BH_Thread *thread,
+ size_t stack,
+ BH_ThreadCallback callback,
+ void *data)
+{
+ struct BH_ThreadContext *context;
+
+ context = malloc(sizeof(*context));
+ if (!context)
+ return BH_ERROR;
+ context->callback = callback;
+ context->data = data;
+
+ thread->handle = (HANDLE)_beginthreadex(NULL, stack, BH_ThreadRun, context, 0, NULL);
+ if (!thread->handle)
+ {
+ free(context);
+ return BH_ERROR;
+ }
+
+ return BH_OK;
+}
+
+
+BH_Thread *BH_ThreadNew(size_t stack,
+ BH_ThreadCallback callback,
+ void *data)
+{
+ BH_Thread *thread;
+
+ thread = malloc(sizeof(BH_Thread));
+ if (thread && BH_ThreadInit(thread, stack, callback, data))
+ {
+ free(thread);
+ return NULL;
+ }
+
+ return thread;
+}
+
+
+int BH_ThreadJoin(BH_Thread *thread)
+{
+ /* Join the thread */
+ WaitForSingleObject(thread->handle, INFINITE);
+ CloseHandle(thread->handle);
+ free(thread);
+
+ return BH_OK;
+}
+
+
+int BH_ThreadDetach(BH_Thread *thread)
+{
+ /* Detach from thread */
+ CloseHandle(thread->handle);
+ free(thread);
+
+ return BH_OK;
+}
diff --git a/src/Platform/Win32/Thread.h b/src/Platform/Win32/Thread.h
new file mode 100644
index 0000000..eb83d1a
--- /dev/null
+++ b/src/Platform/Win32/Thread.h
@@ -0,0 +1,34 @@
+#ifndef BH_PLATFORM_WIN32_THREAD_H
+#define BH_PLATFORM_WIN32_THREAD_H
+
+#include <windows.h>
+
+
+struct BH_Condition
+{
+ CONDITION_VARIABLE handle;
+};
+
+
+struct BH_Mutex
+{
+ CRITICAL_SECTION handle;
+};
+
+
+struct BH_Semaphore
+{
+ HANDLE handle;
+};
+
+
+struct BH_Thread
+{
+ HANDLE handle;
+};
+
+
+void BH_TssCleanup(void);
+
+
+#endif /* BH_PLATFORM_WIN32_THREAD_H */
diff --git a/src/Platform/Win32/Tss.c b/src/Platform/Win32/Tss.c
new file mode 100644
index 0000000..58e2893
--- /dev/null
+++ b/src/Platform/Win32/Tss.c
@@ -0,0 +1,99 @@
+#include "Thread.h"
+
+#include <BH/Thread.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+static int tssCleanupLock = 0;
+static int tssCleanupSize = 0;
+static BH_GenericCallback tssCleanupData[BH_MAX_TSS];
+
+
+static int tssLock = 0;
+static int tssReady = 0;
+static DWORD tssKey;
+
+
+static void __stdcall BH_TssKeyCleanup(void *data)
+{
+ int i;
+
+ /* Lock cleanup table and call cleanups */
+ BH_SpinlockLock(&tssCleanupLock);
+ for (i = 0; i < tssCleanupSize; i++)
+ tssCleanupData[i](((void **)data)[i]);
+ BH_SpinlockUnlock(&tssCleanupLock);
+
+ free(data);
+}
+
+
+static void **BH_TssDataFetch(void)
+{
+ void **result;
+
+ /* Get or setup TSS data */
+ BH_SpinlockLock(&tssLock);
+ if (!tssReady)
+ {
+ tssKey = FlsAlloc(BH_TssKeyCleanup);
+ if (tssKey == FLS_OUT_OF_INDEXES)
+ abort();
+ tssReady = 1;
+ }
+ BH_SpinlockUnlock(&tssLock);
+
+ /* Get or setup TSS data */
+ result = FlsGetValue(tssKey);
+ if (!result)
+ {
+ result = malloc(sizeof(void *) * BH_MAX_TSS);
+ if (!result)
+ abort();
+
+ memset(result, 0, sizeof(sizeof(void *) * BH_MAX_TSS));
+ if (FlsSetValue(tssKey, result))
+ abort();
+ }
+
+ return result;
+}
+
+
+void BH_TssCleanup(void)
+{
+ BH_TssKeyCleanup(BH_TssDataFetch());
+}
+
+
+int BH_TssCreate(BH_GenericCallback callback)
+{
+ int result;
+
+ /* Lock info and set up cleanup function */
+ BH_SpinlockLock(&tssCleanupLock);
+ if (tssCleanupSize < BH_MAX_TSS)
+ {
+ tssCleanupData[tssCleanupSize] = callback;
+ result = tssCleanupSize++;
+ }
+ else
+ result = BH_ERROR;
+ BH_SpinlockUnlock(&tssCleanupLock);
+
+ return result;
+}
+
+
+void *BH_TssRead(int index)
+{
+ return BH_TssDataFetch()[index];
+}
+
+
+void BH_TssWrite(int index,
+ void *value)
+{
+ BH_TssDataFetch()[index] = value;
+}