mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2025-03-11 16:35:33 +00:00
support pthread_key related APIs (#288)
This commit is contained in:
parent
acb68c64c2
commit
e3c04e6684
|
@ -10,6 +10,8 @@
|
|||
#include "../common/wasm_runtime_common.h"
|
||||
#include "thread_manager.h"
|
||||
|
||||
#define WAMR_PTHREAD_KEYS_MAX 32
|
||||
|
||||
#define get_module(exec_env) \
|
||||
wasm_exec_env_get_module(exec_env)
|
||||
|
||||
|
@ -62,10 +64,27 @@ enum cond_status_t {
|
|||
COND_DESTROYED,
|
||||
};
|
||||
|
||||
typedef struct ThreadKeyValueNode {
|
||||
bh_list_link l;
|
||||
wasm_exec_env_t exec_env;
|
||||
int32 thread_key_values[WAMR_PTHREAD_KEYS_MAX];
|
||||
} ThreadKeyValueNode;
|
||||
|
||||
typedef struct KeyData {
|
||||
int32 destructor_func;
|
||||
bool is_created;
|
||||
} KeyData;
|
||||
|
||||
typedef struct ClusterInfoNode {
|
||||
bh_list_link l;
|
||||
WASMCluster *cluster;
|
||||
HashMap *thread_info_map;
|
||||
/* Key data list */
|
||||
KeyData key_data_list[WAMR_PTHREAD_KEYS_MAX];
|
||||
korp_mutex key_data_list_lock;
|
||||
/* Every node contains the key value list for a thread */
|
||||
bh_list thread_list_head;
|
||||
bh_list *thread_list;
|
||||
} ClusterInfoNode;
|
||||
|
||||
typedef struct ThreadInfoNode {
|
||||
|
@ -180,6 +199,132 @@ get_cluster_info(WASMCluster *cluster)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static KeyData*
|
||||
key_data_list_lookup(wasm_exec_env_t exec_env, int32 key)
|
||||
{
|
||||
ClusterInfoNode *node;
|
||||
WASMCluster *cluster =
|
||||
wasm_exec_env_get_cluster(exec_env);
|
||||
|
||||
if ((node = get_cluster_info(cluster))) {
|
||||
return (key >= 0 && key < WAMR_PTHREAD_KEYS_MAX
|
||||
&& node->key_data_list[key].is_created)
|
||||
? &(node->key_data_list[key]) : NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lookup the thread key value node for a thread,
|
||||
create a new one if failed
|
||||
This design will reduce the memory usage. If the thread doesn't use
|
||||
the local storage, it will not occupy memory space
|
||||
*/
|
||||
static int32*
|
||||
key_value_list_lookup_or_create(wasm_exec_env_t exec_env,
|
||||
ClusterInfoNode *info, int32 key)
|
||||
{
|
||||
KeyData *key_node;
|
||||
ThreadKeyValueNode *data;
|
||||
|
||||
/* Check if the key is valid */
|
||||
key_node = key_data_list_lookup(exec_env, key);
|
||||
if (!key_node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find key values node */
|
||||
data = bh_list_first_elem(info->thread_list);
|
||||
while (data) {
|
||||
if (data->exec_env == exec_env)
|
||||
return data->thread_key_values;
|
||||
data = bh_list_elem_next(data);
|
||||
}
|
||||
|
||||
/* If not found, create a new node for this thread */
|
||||
if (!(data = wasm_runtime_malloc(sizeof(ThreadKeyValueNode))))
|
||||
return NULL;
|
||||
memset(data, 0, sizeof(ThreadKeyValueNode));
|
||||
data->exec_env = exec_env;
|
||||
|
||||
if (bh_list_insert(info->thread_list, data) != 0) {
|
||||
wasm_runtime_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return data->thread_key_values;
|
||||
}
|
||||
|
||||
static void
|
||||
call_key_destructor(wasm_exec_env_t exec_env)
|
||||
{
|
||||
int32 i;
|
||||
uint32 destructor_index;
|
||||
KeyData *key_node;
|
||||
ThreadKeyValueNode *value_node;
|
||||
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
|
||||
ClusterInfoNode *info = get_cluster_info(cluster);
|
||||
|
||||
value_node = bh_list_first_elem(info->thread_list);
|
||||
while (value_node) {
|
||||
if (value_node->exec_env == exec_env)
|
||||
break;
|
||||
value_node = bh_list_elem_next(value_node);
|
||||
}
|
||||
|
||||
/* This thread hasn't created key value node */
|
||||
if (!value_node)
|
||||
return;
|
||||
|
||||
/* Destroy key values */
|
||||
for (i = 0; i < WAMR_PTHREAD_KEYS_MAX; i++) {
|
||||
if (value_node->thread_key_values[i] != 0) {
|
||||
int32 value = value_node->thread_key_values[i];
|
||||
os_mutex_lock(&info->key_data_list_lock);
|
||||
|
||||
if ((key_node = key_data_list_lookup(exec_env, i)))
|
||||
destructor_index = key_node->destructor_func;
|
||||
else
|
||||
destructor_index = 0;
|
||||
os_mutex_unlock(&info->key_data_list_lock);
|
||||
|
||||
/* reset key value */
|
||||
value_node->thread_key_values[i] = 0;
|
||||
|
||||
/* Call the destructor func provided by app */
|
||||
if (destructor_index) {
|
||||
uint32 argv[1];
|
||||
|
||||
argv[0] = value;
|
||||
wasm_runtime_call_indirect(exec_env,
|
||||
destructor_index,
|
||||
1, argv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bh_list_remove(info->thread_list, value_node);
|
||||
wasm_runtime_free(value_node);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_thread_key_value_list(bh_list *list)
|
||||
{
|
||||
ThreadKeyValueNode *node, *next;
|
||||
|
||||
/* There should be only one node for main thread */
|
||||
bh_assert(list->len <= 1);
|
||||
|
||||
if (list->len) {
|
||||
node = bh_list_first_elem(list);
|
||||
while (node) {
|
||||
next = bh_list_elem_next(node);
|
||||
call_key_destructor(node->exec_env);
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ClusterInfoNode*
|
||||
create_cluster_info(WASMCluster *cluster)
|
||||
{
|
||||
|
@ -189,6 +334,16 @@ create_cluster_info(WASMCluster *cluster)
|
|||
if (!(node = wasm_runtime_malloc(sizeof(ClusterInfoNode)))) {
|
||||
return NULL;
|
||||
}
|
||||
memset(node, 0, sizeof(WASMCluster));
|
||||
|
||||
node->thread_list = &node->thread_list_head;
|
||||
ret = bh_list_init(node->thread_list);
|
||||
bh_assert(ret == BH_LIST_SUCCESS);
|
||||
|
||||
if (os_mutex_init(&node->key_data_list_lock) != 0) {
|
||||
wasm_runtime_free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->cluster = cluster;
|
||||
if (!(node->thread_info_map =
|
||||
|
@ -197,12 +352,13 @@ create_cluster_info(WASMCluster *cluster)
|
|||
(KeyEqualFunc)thread_handle_equal,
|
||||
NULL,
|
||||
thread_info_destroy))) {
|
||||
os_mutex_destroy(&node->key_data_list_lock);
|
||||
wasm_runtime_free(node);
|
||||
return NULL;
|
||||
}
|
||||
os_mutex_lock(&pthread_global_lock);
|
||||
ret = bh_list_insert(&cluster_info_list, node);
|
||||
bh_assert(ret == 0);
|
||||
bh_assert(ret == BH_LIST_SUCCESS);
|
||||
os_mutex_unlock(&pthread_global_lock);
|
||||
|
||||
(void)ret;
|
||||
|
@ -215,6 +371,10 @@ destroy_cluster_info(WASMCluster *cluster)
|
|||
ClusterInfoNode *node = get_cluster_info(cluster);
|
||||
if (node) {
|
||||
bh_hash_map_destroy(node->thread_info_map);
|
||||
destroy_thread_key_value_list(node->thread_list);
|
||||
os_mutex_destroy(&node->key_data_list_lock);
|
||||
|
||||
/* Remove from the cluster info list */
|
||||
os_mutex_lock(&pthread_global_lock);
|
||||
bh_list_remove(&cluster_info_list, node);
|
||||
wasm_runtime_free(node);
|
||||
|
@ -331,6 +491,9 @@ pthread_start_routine(void *arg)
|
|||
wasm_cluster_spread_exception(exec_env);
|
||||
}
|
||||
|
||||
/* destroy pthread key values */
|
||||
call_key_destructor(exec_env);
|
||||
|
||||
/* routine exit, destroy instance */
|
||||
wasm_runtime_deinstantiate_internal(module_inst, true);
|
||||
|
||||
|
@ -509,6 +672,9 @@ pthread_exit_wrapper(wasm_exec_env_t exec_env, int32 retval_offset)
|
|||
if (!args)
|
||||
return;
|
||||
|
||||
/* destroy pthread key values */
|
||||
call_key_destructor(exec_env);
|
||||
|
||||
/* routine exit, destroy instance */
|
||||
wasm_runtime_deinstantiate_internal(module_inst, true);
|
||||
|
||||
|
@ -702,6 +868,114 @@ pthread_cond_destroy_wrapper(wasm_exec_env_t exec_env, uint32 *cond)
|
|||
return ret_val;
|
||||
}
|
||||
|
||||
static int32
|
||||
pthread_key_create_wrapper(wasm_exec_env_t exec_env, int32 *key,
|
||||
int32 destructor_elem_index)
|
||||
{
|
||||
uint32 i;
|
||||
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
|
||||
ClusterInfoNode *info = get_cluster_info(cluster);
|
||||
|
||||
if (!info) {
|
||||
/* The user may call pthread_key_create in main thread,
|
||||
in this case the cluster info hasn't been created */
|
||||
if (!(info = create_cluster_info(cluster))) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
os_mutex_lock(&info->key_data_list_lock);
|
||||
for (i = 0; i < WAMR_PTHREAD_KEYS_MAX; i++) {
|
||||
if (!info->key_data_list[i].is_created) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == WAMR_PTHREAD_KEYS_MAX) {
|
||||
os_mutex_unlock(&info->key_data_list_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
info->key_data_list[i].destructor_func = destructor_elem_index;
|
||||
info->key_data_list[i].is_created = true;
|
||||
*key = i;
|
||||
os_mutex_unlock(&info->key_data_list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32
|
||||
pthread_setspecific_wrapper(wasm_exec_env_t exec_env, int32 key,
|
||||
int32 value_offset)
|
||||
{
|
||||
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
|
||||
ClusterInfoNode *info = get_cluster_info(cluster);
|
||||
int32 *key_values;
|
||||
|
||||
if (!info)
|
||||
return -1;
|
||||
|
||||
os_mutex_lock(&info->key_data_list_lock);
|
||||
|
||||
key_values = key_value_list_lookup_or_create(exec_env, info, key);
|
||||
if (!key_values) {
|
||||
os_mutex_unlock(&info->key_data_list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
key_values[key] = value_offset;
|
||||
os_mutex_unlock(&info->key_data_list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32
|
||||
pthread_getspecific_wrapper(wasm_exec_env_t exec_env, int32 key)
|
||||
{
|
||||
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
|
||||
ClusterInfoNode *info = get_cluster_info(cluster);
|
||||
int32 ret, *key_values;
|
||||
|
||||
if (!info)
|
||||
return -1;
|
||||
|
||||
os_mutex_lock(&info->key_data_list_lock);
|
||||
|
||||
key_values = key_value_list_lookup_or_create(exec_env, info, key);
|
||||
if (!key_values) {
|
||||
os_mutex_unlock(&info->key_data_list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = key_values[key];
|
||||
os_mutex_unlock(&info->key_data_list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int32
|
||||
pthread_key_delete_wrapper(wasm_exec_env_t exec_env, int32 key)
|
||||
{
|
||||
KeyData *data;
|
||||
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
|
||||
ClusterInfoNode *info = get_cluster_info(cluster);
|
||||
|
||||
if (!info)
|
||||
return -1;
|
||||
|
||||
os_mutex_lock(&info->key_data_list_lock);
|
||||
data = key_data_list_lookup(exec_env, key);
|
||||
if (!data) {
|
||||
os_mutex_unlock(&info->key_data_list_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(data, 0, sizeof(KeyData));
|
||||
os_mutex_unlock(&info->key_data_list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define REG_NATIVE_FUNC(func_name, signature) \
|
||||
{ #func_name, func_name##_wrapper, signature, NULL }
|
||||
|
||||
|
@ -721,6 +995,10 @@ static NativeSymbol native_symbols_lib_pthread[] = {
|
|||
REG_NATIVE_FUNC(pthread_cond_timedwait, "(**i)i"),
|
||||
REG_NATIVE_FUNC(pthread_cond_signal, "(*)i"),
|
||||
REG_NATIVE_FUNC(pthread_cond_destroy, "(*)i"),
|
||||
REG_NATIVE_FUNC(pthread_key_create, "(*i)i"),
|
||||
REG_NATIVE_FUNC(pthread_setspecific, "(ii)i"),
|
||||
REG_NATIVE_FUNC(pthread_getspecific, "(i)i"),
|
||||
REG_NATIVE_FUNC(pthread_key_delete, "(i)i"),
|
||||
};
|
||||
|
||||
uint32
|
||||
|
|
|
@ -47,7 +47,7 @@ To build this C program into WebAssembly app with libc-builtin, you can use this
|
|||
-Wl,--export=__wasm_call_ctors \
|
||||
main.c -o test.wasm
|
||||
# -pthread: it will enable some dependent WebAssembly features for thread
|
||||
# -nostdlib: disable the WASI standard library as we only support libc-builtin currently
|
||||
# -nostdlib: disable the WASI standard library as we are using WAMR builtin-libc
|
||||
# -z stack-size=: specify the total aux stack size
|
||||
# -Wl,--export=__heap_base,--export=__data_end: export these globals so the runtime can resolve the total aux stack size and the start offset of the stack top
|
||||
# -Wl,--export=__wasm_call_ctors: export the init function to initialize the passive data segments
|
||||
|
@ -149,6 +149,15 @@ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
|||
int pthread_cond_signal(pthread_cond_t *cond);
|
||||
|
||||
int pthread_cond_destroy(pthread_cond_t *cond);
|
||||
|
||||
/* Pthread key APIs */
|
||||
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));
|
||||
|
||||
int pthread_setspecific(pthread_key_t key, const void *value);
|
||||
|
||||
void *pthread_getspecific(pthread_key_t key);
|
||||
|
||||
int pthread_key_delete(pthread_key_t key);
|
||||
```
|
||||
|
||||
## Known limits
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
#ifndef _WAMR_LIB_PTHREAD_H
|
||||
#define _WAMR_LIB_PTHREAD_H
|
||||
|
||||
/* Data type define of pthread, mutex and cond */
|
||||
/* Data type define of pthread, mutex, cond and key */
|
||||
typedef unsigned int pthread_t;
|
||||
typedef unsigned int pthread_mutex_t;
|
||||
typedef unsigned int pthread_cond_t;
|
||||
typedef unsigned int pthread_key_t;
|
||||
|
||||
/* Thread APIs */
|
||||
int pthread_create(pthread_t *thread, const void *attr,
|
||||
|
@ -46,4 +47,13 @@ int pthread_cond_signal(pthread_cond_t *cond);
|
|||
|
||||
int pthread_cond_destroy(pthread_cond_t *cond);
|
||||
|
||||
/* Pthread key APIs */
|
||||
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));
|
||||
|
||||
int pthread_setspecific(pthread_key_t key, const void *value);
|
||||
|
||||
void *pthread_getspecific(pthread_key_t key);
|
||||
|
||||
int pthread_key_delete(pthread_key_t key);
|
||||
|
||||
#endif /* end of _WAMR_LIB_PTHREAD_H */
|
|
@ -77,3 +77,7 @@ pthread_cond_wait
|
|||
pthread_cond_timedwait
|
||||
pthread_cond_signal
|
||||
pthread_cond_destroy
|
||||
pthread_key_create
|
||||
pthread_setspecific
|
||||
pthread_getspecific
|
||||
pthread_key_delete
|
||||
|
|
Loading…
Reference in New Issue
Block a user