From e3c04e66841df6025a465c19233fc7fd4cec7ab7 Mon Sep 17 00:00:00 2001 From: Xu Jun <693788454@qq.com> Date: Thu, 18 Jun 2020 11:37:31 +0800 Subject: [PATCH] support pthread_key related APIs (#288) --- .../lib-pthread/lib_pthread_wrapper.c | 280 +++++++++++++++++- doc/pthread_library.md | 11 +- .../libc-builtin-sysroot/include/pthread.h | 12 +- .../share/defined-symbols.txt | 4 + 4 files changed, 304 insertions(+), 3 deletions(-) diff --git a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c index 9f19f8fc..f7aa9083 100644 --- a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c +++ b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c @@ -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 diff --git a/doc/pthread_library.md b/doc/pthread_library.md index 4e9409f3..622bdfa2 100644 --- a/doc/pthread_library.md +++ b/doc/pthread_library.md @@ -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 diff --git a/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h b/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h index b10f4b59..38be34e1 100644 --- a/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h +++ b/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h @@ -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 */ \ No newline at end of file diff --git a/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt b/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt index 2b878b84..08a37846 100644 --- a/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt +++ b/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt @@ -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