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).
This commit is contained in:
2025-03-02 23:18:23 +03:00
parent 2ca6a3e316
commit d403d41f2c
21 changed files with 1492 additions and 9 deletions

View File

@@ -51,6 +51,7 @@ set(ENABLE_TESTING ON CACHE BOOL "Enable unit-testing")
set(ENABLE_COVERAGE OFF CACHE BOOL "Enable coverage") set(ENABLE_COVERAGE OFF CACHE BOOL "Enable coverage")
set(ENABLE_EXAMPLES ON CACHE BOOL "Enable building examples") set(ENABLE_EXAMPLES ON CACHE BOOL "Enable building examples")
set(ENABLE_LTO ON CACHE BOOL "Enable LTO support") set(ENABLE_LTO ON CACHE BOOL "Enable LTO support")
set(ENABLE_MT ON CACHE BOOL "Enable multithreading support")
# Enable IPO/LTO # Enable IPO/LTO
if(ENABLE_LTO) if(ENABLE_LTO)
@@ -102,6 +103,57 @@ set(BH_HEADER
include/BH/Math.h include/BH/Math.h
include/BH/Queue.h include/BH/Queue.h
include/BH/Util.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 set(BH_INCLUDE_DIRS
@@ -114,23 +166,36 @@ if(WIN32)
message(STATUS "Platform - Win32") message(STATUS "Platform - Win32")
# Add platform dependent files # Add platform dependent files
list(APPEND BH_SOURCE list(APPEND BH_SOURCE ${BH_SOURCE_WIN32})
src/Platform/Win32/File.c
) # 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) elseif(UNIX)
message(STATUS "Platform: Unix (Linux/BSD/MacOS X)") message(STATUS "Platform: Unix (Linux/BSD/MacOS X)")
# Add platform dependent files # Add platform dependent files
list(APPEND BH_SOURCE list(APPEND BH_SOURCE BH_SOURCE_POSIX)
src/Platform/Posix/File.c
) # 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() else()
message(STATUS "Platform: Unknown") message(STATUS "Platform: Unknown")
# Add platform dependent files # Add platform dependent files
list(APPEND BH_SOURCE list(APPEND BH_SOURCE ${BH_SOURCE_DUMMY} ${BH_SOURCE_DUMMY_MT})
src/Platform/Dummy/File.c list(APPEND BH_HEADER ${BH_HEADER_DUMMY_MT})
)
endif() endif()
# Library # Library

302
include/BH/Thread.h Normal file
View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 */

24
src/Platform/Dummy/Tss.c Normal file
View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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 */

97
src/Platform/Posix/Tss.c Normal file
View File

@@ -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;
}

50
src/Platform/Spinlock.c Normal file
View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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 */

99
src/Platform/Win32/Tss.c Normal file
View File

@@ -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;
}