瀏覽代碼

support pthread_key related APIs (#288)

Xu Jun 5 年之前
父節點
當前提交
e3c04e6684

+ 279 - 1
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

+ 10 - 1
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

+ 11 - 1
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 */

+ 4 - 0
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