diff options
| -rw-r--r-- | CMakeLists.txt | 83 | ||||
| -rw-r--r-- | include/BH/Thread.h | 302 | ||||
| -rw-r--r-- | src/Platform/Dummy/Condition.c | 53 | ||||
| -rw-r--r-- | src/Platform/Dummy/Mutex.c | 39 | ||||
| -rw-r--r-- | src/Platform/Dummy/Semaphore.c | 51 | ||||
| -rw-r--r-- | src/Platform/Dummy/Thread.c | 31 | ||||
| -rw-r--r-- | src/Platform/Dummy/Thread.h | 33 | ||||
| -rw-r--r-- | src/Platform/Dummy/Tss.c | 24 | ||||
| -rw-r--r-- | src/Platform/Posix/Condition.c | 74 | ||||
| -rw-r--r-- | src/Platform/Posix/Mutex.c | 57 | ||||
| -rw-r--r-- | src/Platform/Posix/Semaphore.c | 73 | ||||
| -rw-r--r-- | src/Platform/Posix/Thread.c | 98 | ||||
| -rw-r--r-- | src/Platform/Posix/Thread.h | 35 | ||||
| -rw-r--r-- | src/Platform/Posix/Tss.c | 97 | ||||
| -rw-r--r-- | src/Platform/Spinlock.c | 50 | ||||
| -rw-r--r-- | src/Platform/Win32/Condition.c | 58 | ||||
| -rw-r--r-- | src/Platform/Win32/Mutex.c | 51 | ||||
| -rw-r--r-- | src/Platform/Win32/Semaphore.c | 69 | ||||
| -rw-r--r-- | src/Platform/Win32/Thread.c | 90 | ||||
| -rw-r--r-- | src/Platform/Win32/Thread.h | 34 | ||||
| -rw-r--r-- | src/Platform/Win32/Tss.c | 99 |
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; +} |
