aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/bh/bh.h1
-rw-r--r--include/bh/internal/thread.h24
-rw-r--r--include/bh/internal/thread_null.h7
-rw-r--r--include/bh/internal/thread_posix.h5
-rw-r--r--include/bh/internal/thread_win.h5
-rw-r--r--include/bh/thread.h17
-rw-r--r--src/thread.c68
-rw-r--r--src/thread_null.c39
-rw-r--r--src/thread_posix.c106
-rw-r--r--src/thread_win.c90
10 files changed, 359 insertions, 3 deletions
diff --git a/include/bh/bh.h b/include/bh/bh.h
index 277728e..a56e169 100644
--- a/include/bh/bh.h
+++ b/include/bh/bh.h
@@ -27,6 +27,7 @@
#define BH_NOT_FOUND 0x0006
#define BH_TIMEOUT 0x0007
+typedef void (*bh_generic_cb_t)(void *data);
typedef int (*bh_equal_cb_t)(const void *, const void *);
typedef size_t (*bh_hash_cb_t)(const void *);
diff --git a/include/bh/internal/thread.h b/include/bh/internal/thread.h
index 34e2f76..8ef9487 100644
--- a/include/bh/internal/thread.h
+++ b/include/bh/internal/thread.h
@@ -6,6 +6,8 @@
#define BH_THREAD_DONE 0x0100
+#define BH_MAX_TLS 32
+
#if defined(BH_USE_PTHREAD)
#include "thread_posix.h"
#elif defined(BH_USE_WINTHREAD)
@@ -33,6 +35,18 @@ struct bh_thread_pool_s
int shutdown;
};
+typedef struct bh_tls_info_s
+{
+ size_t counter;
+ bh_spinlock_t lock;
+ bh_generic_cb_t cleanup[BH_MAX_TLS];
+} bh_tls_info_t;
+
+typedef struct bh_tls_s
+{
+ void *data[BH_MAX_TLS];
+} bh_tls_t;
+
void bh_task_init(bh_task_t *task,
void (*func)(void *),
void *data,
@@ -42,4 +56,14 @@ void bh_task_destroy(bh_task_t *task);
void bh_thread_pool_worker(void *arg);
+void bh_spinlock_init(bh_spinlock_t *lock);
+
+void bh_spinlock_destroy(bh_spinlock_t *lock);
+
+bh_tls_info_t *bh_tls_info(void);
+
+bh_tls_t *bh_tls_fetch(void);
+
+void bh_tls_cleanup(void);
+
#endif /* BH_INTERNAL_THREAD_H */
diff --git a/include/bh/internal/thread_null.h b/include/bh/internal/thread_null.h
index 035d585..bcf476f 100644
--- a/include/bh/internal/thread_null.h
+++ b/include/bh/internal/thread_null.h
@@ -18,6 +18,11 @@ struct bh_cond_s
void *handle;
};
+struct bh_spinlock_s
+{
+ int handle;
+};
+
int bh_thread_init(bh_thread_t *thread,
bh_task_t *task);
@@ -30,4 +35,4 @@ void bh_cond_destroy(bh_cond_t *cond);
int bh_thread_pool_init(bh_thread_pool_t *pool,
size_t size);
-void bh_thread_pool_destroy(bh_thread_pool_t *pool);
+void bh_thread_pool_destroy(bh_thread_pool_t *pool); \ No newline at end of file
diff --git a/include/bh/internal/thread_posix.h b/include/bh/internal/thread_posix.h
index f02ce3a..0226efd 100644
--- a/include/bh/internal/thread_posix.h
+++ b/include/bh/internal/thread_posix.h
@@ -22,6 +22,11 @@ struct bh_cond_s
pthread_cond_t handle;
};
+struct bh_spinlock_s
+{
+ int handle;
+};
+
int bh_thread_init(bh_thread_t *thread,
bh_task_t *task);
diff --git a/include/bh/internal/thread_win.h b/include/bh/internal/thread_win.h
index 46365c8..d9d1f7e 100644
--- a/include/bh/internal/thread_win.h
+++ b/include/bh/internal/thread_win.h
@@ -39,6 +39,11 @@ struct bh_cond_s
};
#endif
+struct bh_spinlock_s
+{
+ LONG handle;
+};
+
int bh_thread_init_base(bh_thread_t *thread,
bh_task_t *task,
bh_thread_begin_cb_t begin,
diff --git a/include/bh/thread.h b/include/bh/thread.h
index 22bdf30..3360c42 100644
--- a/include/bh/thread.h
+++ b/include/bh/thread.h
@@ -12,6 +12,7 @@ typedef struct bh_semaphore_s bh_semaphore_t;
typedef struct bh_cond_s bh_cond_t;
typedef struct bh_task_s bh_task_t;
typedef struct bh_thread_pool_s bh_thread_pool_t;
+typedef struct bh_spinlock_s bh_spinlock_t;
#if defined(BH_USE_PTHREAD)
bh_thread_t *bh_thread_new(bh_task_t *task);
@@ -108,4 +109,20 @@ int bh_thread_pool_wait(bh_thread_pool_t *pool);
void bh_thread_pool_free(bh_thread_pool_t *pool);
+bh_spinlock_t *bh_spinlock_new(void);
+
+void bh_spinlock_free(bh_spinlock_t *lock);
+
+int bh_spinlock_lock(bh_spinlock_t *lock);
+
+int bh_spinlock_unlock(bh_spinlock_t *lock);
+
+int bh_spinlock_try_lock(bh_spinlock_t *lock);
+
+int bh_tls_alloc(bh_generic_cb_t cleanup);
+
+void *bh_tls_get(int slot);
+
+void bh_tls_set(int slot, void* data);
+
#endif /* BH_THREAD_H */
diff --git a/src/thread.c b/src/thread.c
index c3e536c..92edf5a 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -373,6 +373,74 @@ void bh_thread_pool_worker(void *arg)
}
}
+
+bh_spinlock_t *bh_spinlock_new(void)
+{
+ bh_spinlock_t *result;
+
+ result = malloc(sizeof(*result));
+ if (result)
+ bh_spinlock_init(result);
+
+ return result;
+}
+
+void bh_spinlock_free(bh_spinlock_t *lock)
+{
+ bh_spinlock_destroy(lock);
+ free(lock);
+}
+
+int bh_tls_alloc(bh_generic_cb_t cleanup)
+{
+ bh_tls_info_t *info;
+ int result;
+
+ /* Fetch TLS managment info */
+ info = bh_tls_info();
+ result = BH_ERROR;
+
+ /* Get new counter value and setup cleanup function */
+ bh_spinlock_lock(&info->lock);
+ if (info->counter < BH_MAX_TLS)
+ {
+ result = info->counter++;
+ info->cleanup[result] = cleanup;
+ }
+ bh_spinlock_unlock(&info->lock);
+
+ /* Return slot id */
+ return result;
+}
+
+void *bh_tls_get(int slot)
+{
+ return bh_tls_fetch()->data[slot];
+}
+
+void bh_tls_set(int slot, void *data)
+{
+ bh_tls_fetch()->data[slot] = data;
+}
+
+void bh_tls_cleanup(void)
+{
+ bh_tls_info_t *info;
+ bh_tls_t *tls;
+ size_t i;
+
+ /* Fetch TLS managment info and TLS itself */
+ info = bh_tls_info();
+ tls = bh_tls_fetch();
+
+ /* Call cleanup for every slot */
+ for (i = 0; i < info->counter; i++)
+ if (info->cleanup[i])
+ info->cleanup[i](tls->data[i]);
+
+ /* Free tls */
+ free(tls);
+}
/**
* \}
*/ \ No newline at end of file
diff --git a/src/thread_null.c b/src/thread_null.c
index 25d8a88..212174a 100644
--- a/src/thread_null.c
+++ b/src/thread_null.c
@@ -367,6 +367,45 @@ int bh_thread_pool_init(bh_thread_pool_t *pool,
return BH_NO_IMPL;
}
+void bh_spinlock_init(bh_spinlock_t *lock)
+{
+ lock->handle = 0;
+}
+
+void bh_spinlock_destroy(bh_spinlock_t *lock)
+{
+ (void)lock;
+}
+
+int bh_spinlock_lock(bh_spinlock_t *lock)
+{
+ return BH_NO_IMPL;
+}
+
+int bh_spinlock_unlock(bh_spinlock_t *lock)
+{
+ return BH_NO_IMPL;
+}
+
+int bh_spinlock_try_lock(bh_spinlock_t *lock)
+{
+ return BH_NO_IMPL;
+}
+
+bh_tls_info_t *bh_tls_info(void)
+{
+ static bh_tls_info_t info;
+
+ return &info;
+}
+
+bh_tls_t *bh_tls_fetch(void)
+{
+ static bh_tls_t tls;
+
+ return &tls;
+}
+
/**
* \}
*/ \ No newline at end of file
diff --git a/src/thread_posix.c b/src/thread_posix.c
index 2898644..84e8e09 100644
--- a/src/thread_posix.c
+++ b/src/thread_posix.c
@@ -4,9 +4,15 @@
static void *bh_thread_run(void *arg)
{
+ bh_tls_t *tls;
bh_task_t *task;
+
+ /* Fetch thread data */
task = (bh_task_t *)arg;
+ /* Setup TLS */
+ tls = bh_tls_fetch();
+
/* Do the task, mark as done, and if required free it */
task->func(task->data);
task->flags |= BH_THREAD_DONE;
@@ -14,6 +20,9 @@ static void *bh_thread_run(void *arg)
if (task->flags & BH_THREAD_CLEANUP)
bh_task_free(task);
+ /* Destroy TLS */
+ bh_tls_cleanup();
+
return NULL;
}
@@ -283,3 +292,100 @@ bh_thread_pool_t *bh_thread_pool_new(size_t size)
return result;
}
+
+void bh_spinlock_init(bh_spinlock_t *lock)
+{
+ lock->handle = 0;
+}
+
+void bh_spinlock_destroy(bh_spinlock_t *lock)
+{
+ (void)lock;
+}
+
+int bh_spinlock_lock(bh_spinlock_t *lock)
+{
+ /* TODO: Check on other OS and compilers */
+#if defined(__clang__) || defined(__GNUC__)
+ while (__sync_lock_test_and_set(&lock->handle, 1));
+ return BH_OK;
+#else
+#error "Spinlocks are not supported"
+ return BH_NO_IMPL;
+#endif
+}
+
+int bh_spinlock_unlock(bh_spinlock_t *lock)
+{
+ /* TODO: Check on other OS and compilers */
+#if defined(__clang__) || defined(__GNUC__)
+ if (__sync_lock_test_and_set(&lock->handle, 0))
+ return BH_OK;
+ return BH_ERROR;
+#else
+#error "Spinlocks are not supported"
+ return BH_NO_IMPL;
+#endif
+}
+
+int bh_spinlock_try_lock(bh_spinlock_t *lock)
+{
+ /* TODO: Check on other OSs and compilers */
+#if defined(__clang__) || defined(__GNUC__)
+ if (__sync_lock_test_and_set(&lock->handle, 1))
+ return BH_ERROR;
+ return BH_OK;
+#else
+#error "Spinlocks are not supported"
+ return BH_NO_IMPL;
+#endif
+}
+
+bh_tls_info_t *bh_tls_info(void)
+{
+ static bh_tls_info_t info = { 0, 0 };
+
+ return &info;
+}
+
+bh_tls_t *bh_tls_fetch(void)
+{
+ static bh_spinlock_t tls_lock = { 0 };
+ static int tls_ready = 0;
+ static pthread_key_t tls_key;
+
+ bh_tls_t *tls;
+
+ /* Protect pthread key by spinlock */
+ bh_spinlock_lock(&tls_lock);
+
+ /* Allocate pthread key if it's not allocated */
+ if (tls_ready == 0)
+ {
+ pthread_key_create(&tls_key, NULL);
+ tls_ready = 1;
+ }
+
+ /* Unlock spinlock */
+ bh_spinlock_unlock(&tls_lock);
+
+ /* Get TLS pointer and setup TLS if neccesery */
+ tls = (bh_tls_t *)pthread_getspecific(tls_key);
+ if (tls == NULL)
+ {
+ /* Allocate space for TLS */
+ tls = malloc(sizeof(*tls));
+ if (!tls)
+ abort();
+
+ /* Zero out TLS */
+ memset(tls, 0, sizeof(*tls));
+
+ /* Set TLS value */
+ if (pthread_setspecific(tls_key, tls))
+ abort();
+ }
+
+ /* Return TLS */
+ return tls;
+} \ No newline at end of file
diff --git a/src/thread_win.c b/src/thread_win.c
index bb14774..92a5bb4 100644
--- a/src/thread_win.c
+++ b/src/thread_win.c
@@ -2,12 +2,16 @@
static unsigned __stdcall bh_thread_run(void *arg)
{
+ bh_tls_t *tls;
bh_thread_data_t data;
/* Fetch thread data, store it on stack and free from heap */
data = *(bh_thread_data_t *)arg;
free(arg);
+ /* Setup TLS */
+ tls = bh_tls_fetch();
+
/* Do the task, mark as done, and if required free it */
data.task->func(data.task->data);
data.task->flags |= BH_THREAD_DONE;
@@ -15,8 +19,12 @@ static unsigned __stdcall bh_thread_run(void *arg)
if (data.task->flags & BH_THREAD_CLEANUP)
bh_task_free(data.task);
- /* Call thread specific end function (deallocate TLS) */
+ /* Destroy our TLS */
+ bh_tls_cleanup();
+
+ /* Call thread specific end function (deallocate libc TLS) */
data.end(0);
+
return 0;
}
@@ -58,7 +66,7 @@ bh_thread_t *bh_thread_new_base(bh_task_t *task,
/* Allocate thread object */
result = malloc(sizeof(*result));
- if (result && !bh_thread_init_base(result, task, begin, end))
+ if (result && bh_thread_init_base(result, task, begin, end))
{
free(result);
result = NULL;
@@ -439,3 +447,81 @@ bh_thread_pool_t *bh_thread_pool_new_base(size_t size,
return result;
}
+void bh_spinlock_init(bh_spinlock_t *lock)
+{
+ lock->handle = 0;
+}
+
+void bh_spinlock_destroy(bh_spinlock_t *lock)
+{
+ (void)lock;
+}
+
+int bh_spinlock_lock(bh_spinlock_t *lock)
+{
+ while (InterlockedExchange(&lock->handle, 1));
+ return BH_OK;
+}
+
+int bh_spinlock_unlock(bh_spinlock_t *lock)
+{
+ if (InterlockedExchange(&lock->handle, 0))
+ return BH_OK;
+ return BH_ERROR;
+}
+
+int bh_spinlock_try_lock(bh_spinlock_t *lock)
+{
+ if (InterlockedExchange(&lock->handle, 1))
+ return BH_ERROR;
+ return BH_OK;
+}
+
+bh_tls_info_t *bh_tls_info(void)
+{
+ static bh_tls_info_t info = {0, 0};
+
+ return &info;
+}
+
+bh_tls_t *bh_tls_fetch(void)
+{
+ static bh_spinlock_t tls_lock = {0};
+ static DWORD tls_index = TLS_OUT_OF_INDEXES;
+
+ bh_tls_t *tls;
+
+ /* Protect TLS index by spinlock */
+ bh_spinlock_lock(&tls_lock);
+
+ /* Allocate TLS index if it's not allocated */
+ if (tls_index == TLS_OUT_OF_INDEXES)
+ tls_index = TlsAlloc();
+
+ /* Unlock spinlock */
+ bh_spinlock_unlock(&tls_lock);
+
+ /* Check that TLS index is valid */
+ if (tls_index == TLS_OUT_OF_INDEXES)
+ abort();
+
+ /* Get TLS pointer and setup TLS if neccesery */
+ tls = (bh_tls_t *)TlsGetValue(tls_index);
+ if (tls == NULL)
+ {
+ /* Allocate space for TLS */
+ tls = malloc(sizeof(*tls));
+ if (!tls)
+ abort();
+
+ /* Zero out TLS */
+ memset(tls, 0, sizeof(*tls));
+
+ /* Set TLS value */
+ if (!TlsSetValue(tls_index, tls))
+ abort();
+ }
+
+ /* Return TLS */
+ return tls;
+}