mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2025-05-09 13:16:26 +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 "../common/wasm_runtime_common.h"
|
||||||
#include "thread_manager.h"
|
#include "thread_manager.h"
|
||||||
|
|
||||||
|
#define WAMR_PTHREAD_KEYS_MAX 32
|
||||||
|
|
||||||
#define get_module(exec_env) \
|
#define get_module(exec_env) \
|
||||||
wasm_exec_env_get_module(exec_env)
|
wasm_exec_env_get_module(exec_env)
|
||||||
|
|
||||||
|
@ -62,10 +64,27 @@ enum cond_status_t {
|
||||||
COND_DESTROYED,
|
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 {
|
typedef struct ClusterInfoNode {
|
||||||
bh_list_link l;
|
bh_list_link l;
|
||||||
WASMCluster *cluster;
|
WASMCluster *cluster;
|
||||||
HashMap *thread_info_map;
|
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;
|
} ClusterInfoNode;
|
||||||
|
|
||||||
typedef struct ThreadInfoNode {
|
typedef struct ThreadInfoNode {
|
||||||
|
@ -180,6 +199,132 @@ get_cluster_info(WASMCluster *cluster)
|
||||||
return NULL;
|
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*
|
static ClusterInfoNode*
|
||||||
create_cluster_info(WASMCluster *cluster)
|
create_cluster_info(WASMCluster *cluster)
|
||||||
{
|
{
|
||||||
|
@ -189,6 +334,16 @@ create_cluster_info(WASMCluster *cluster)
|
||||||
if (!(node = wasm_runtime_malloc(sizeof(ClusterInfoNode)))) {
|
if (!(node = wasm_runtime_malloc(sizeof(ClusterInfoNode)))) {
|
||||||
return NULL;
|
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;
|
node->cluster = cluster;
|
||||||
if (!(node->thread_info_map =
|
if (!(node->thread_info_map =
|
||||||
|
@ -197,12 +352,13 @@ create_cluster_info(WASMCluster *cluster)
|
||||||
(KeyEqualFunc)thread_handle_equal,
|
(KeyEqualFunc)thread_handle_equal,
|
||||||
NULL,
|
NULL,
|
||||||
thread_info_destroy))) {
|
thread_info_destroy))) {
|
||||||
|
os_mutex_destroy(&node->key_data_list_lock);
|
||||||
wasm_runtime_free(node);
|
wasm_runtime_free(node);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
os_mutex_lock(&pthread_global_lock);
|
os_mutex_lock(&pthread_global_lock);
|
||||||
ret = bh_list_insert(&cluster_info_list, node);
|
ret = bh_list_insert(&cluster_info_list, node);
|
||||||
bh_assert(ret == 0);
|
bh_assert(ret == BH_LIST_SUCCESS);
|
||||||
os_mutex_unlock(&pthread_global_lock);
|
os_mutex_unlock(&pthread_global_lock);
|
||||||
|
|
||||||
(void)ret;
|
(void)ret;
|
||||||
|
@ -215,6 +371,10 @@ destroy_cluster_info(WASMCluster *cluster)
|
||||||
ClusterInfoNode *node = get_cluster_info(cluster);
|
ClusterInfoNode *node = get_cluster_info(cluster);
|
||||||
if (node) {
|
if (node) {
|
||||||
bh_hash_map_destroy(node->thread_info_map);
|
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);
|
os_mutex_lock(&pthread_global_lock);
|
||||||
bh_list_remove(&cluster_info_list, node);
|
bh_list_remove(&cluster_info_list, node);
|
||||||
wasm_runtime_free(node);
|
wasm_runtime_free(node);
|
||||||
|
@ -331,6 +491,9 @@ pthread_start_routine(void *arg)
|
||||||
wasm_cluster_spread_exception(exec_env);
|
wasm_cluster_spread_exception(exec_env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* destroy pthread key values */
|
||||||
|
call_key_destructor(exec_env);
|
||||||
|
|
||||||
/* routine exit, destroy instance */
|
/* routine exit, destroy instance */
|
||||||
wasm_runtime_deinstantiate_internal(module_inst, true);
|
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)
|
if (!args)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* destroy pthread key values */
|
||||||
|
call_key_destructor(exec_env);
|
||||||
|
|
||||||
/* routine exit, destroy instance */
|
/* routine exit, destroy instance */
|
||||||
wasm_runtime_deinstantiate_internal(module_inst, true);
|
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;
|
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) \
|
#define REG_NATIVE_FUNC(func_name, signature) \
|
||||||
{ #func_name, func_name##_wrapper, signature, NULL }
|
{ #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_timedwait, "(**i)i"),
|
||||||
REG_NATIVE_FUNC(pthread_cond_signal, "(*)i"),
|
REG_NATIVE_FUNC(pthread_cond_signal, "(*)i"),
|
||||||
REG_NATIVE_FUNC(pthread_cond_destroy, "(*)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
|
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 \
|
-Wl,--export=__wasm_call_ctors \
|
||||||
main.c -o test.wasm
|
main.c -o test.wasm
|
||||||
# -pthread: it will enable some dependent WebAssembly features for thread
|
# -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
|
# -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=__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
|
# -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_signal(pthread_cond_t *cond);
|
||||||
|
|
||||||
int pthread_cond_destroy(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
|
## Known limits
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
#ifndef _WAMR_LIB_PTHREAD_H
|
#ifndef _WAMR_LIB_PTHREAD_H
|
||||||
#define _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_t;
|
||||||
typedef unsigned int pthread_mutex_t;
|
typedef unsigned int pthread_mutex_t;
|
||||||
typedef unsigned int pthread_cond_t;
|
typedef unsigned int pthread_cond_t;
|
||||||
|
typedef unsigned int pthread_key_t;
|
||||||
|
|
||||||
/* Thread APIs */
|
/* Thread APIs */
|
||||||
int pthread_create(pthread_t *thread, const void *attr,
|
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);
|
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 */
|
#endif /* end of _WAMR_LIB_PTHREAD_H */
|
|
@ -77,3 +77,7 @@ pthread_cond_wait
|
||||||
pthread_cond_timedwait
|
pthread_cond_timedwait
|
||||||
pthread_cond_signal
|
pthread_cond_signal
|
||||||
pthread_cond_destroy
|
pthread_cond_destroy
|
||||||
|
pthread_key_create
|
||||||
|
pthread_setspecific
|
||||||
|
pthread_getspecific
|
||||||
|
pthread_key_delete
|
||||||
|
|
Loading…
Reference in New Issue
Block a user