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