support pthread_key related APIs (#288)

This commit is contained in:
Xu Jun 2020-06-18 11:37:31 +08:00 committed by GitHub
parent acb68c64c2
commit e3c04e6684
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 304 additions and 3 deletions

View File

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

View File

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

View File

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

View File

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