| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731 |
- /*
- * Copyright (C) 2019 Intel Corporation. All rights reserved.
- * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- */
- #include "bh_common.h"
- #include "bh_log.h"
- #include "wasm_export.h"
- #include "../interpreter/wasm.h"
- #include "../common/wasm_runtime_common.h"
- #include "thread_manager.h"
- #define get_module(exec_env) \
- wasm_exec_env_get_module(exec_env)
- #define get_module_inst(exec_env) \
- wasm_runtime_get_module_inst(exec_env)
- #define get_thread_arg(exec_env) \
- wasm_exec_env_get_thread_arg(exec_env)
- #define get_wasi_ctx(module_inst) \
- wasm_runtime_get_wasi_ctx(module_inst)
- #define validate_app_addr(offset, size) \
- wasm_runtime_validate_app_addr(module_inst, offset, size)
- #define validate_native_addr(addr, size) \
- wasm_runtime_validate_native_addr(module_inst, addr, size)
- #define addr_app_to_native(offset) \
- wasm_runtime_addr_app_to_native(module_inst, offset)
- #define addr_native_to_app(ptr) \
- wasm_runtime_addr_native_to_app(module_inst, ptr)
- extern bool
- wasm_runtime_call_indirect(wasm_exec_env_t exec_env,
- uint32 element_indices,
- uint32 argc, uint32 argv[]);
- enum {
- T_THREAD,
- T_MUTEX,
- T_COND,
- };
- enum thread_status_t {
- THREAD_INIT,
- THREAD_RUNNING,
- THREAD_CANCELLED,
- THREAD_EXIT,
- };
- enum mutex_status_t {
- MUTEX_CREATED,
- MUTEX_DESTROYED,
- };
- enum cond_status_t {
- COND_CREATED,
- COND_DESTROYED,
- };
- typedef struct ClusterInfoNode {
- bh_list_link l;
- WASMCluster *cluster;
- HashMap *thread_info_map;
- } ClusterInfoNode;
- typedef struct ThreadInfoNode {
- wasm_exec_env_t parent_exec_env;
- wasm_exec_env_t exec_env;
- /* the id returned to app */
- uint32 handle;
- /* type can be [THREAD | MUTEX | CONDITION] */
- uint32 type;
- /* Thread status, this variable should be volatile
- as its value may be changed in different threads */
- volatile uint32 status;
- union {
- korp_tid thread;
- korp_mutex *mutex;
- korp_cond *cond;
- } u;
- } ThreadInfoNode;
- typedef struct {
- ThreadInfoNode *info_node;
- /* table elem index of the app's entry function */
- uint32 elem_index;
- /* arg of the app's entry function */
- void *arg;
- wasm_module_inst_t module_inst;
- } ThreadRoutineArgs;
- static bh_list cluster_info_list;
- static korp_mutex pthread_global_lock;
- static uint32 handle_id = 1;
- static void
- lib_pthread_destroy_callback(WASMCluster *cluster);
- static uint32
- thread_handle_hash(void *handle)
- {
- return (uint32)(uintptr_t)handle;
- }
- static bool
- thread_handle_equal(void *h1, void *h2)
- {
- return (uint32)(uintptr_t)h1 == (uint32)(uintptr_t)h2 ? true : false;
- }
- static void
- thread_info_destroy(void *node)
- {
- ThreadInfoNode *info_node = (ThreadInfoNode *)node;
- ThreadRoutineArgs *args;
- pthread_mutex_lock(&pthread_global_lock);
- if (info_node->type == T_THREAD) {
- args = get_thread_arg(info_node->exec_env);
- if (args) {
- wasm_runtime_free(args);
- }
- }
- else if (info_node->type == T_MUTEX) {
- if (info_node->status != MUTEX_DESTROYED)
- os_mutex_destroy(info_node->u.mutex);
- wasm_runtime_free(info_node->u.mutex);
- }
- else if (info_node->type == T_COND) {
- if (info_node->status != COND_DESTROYED)
- os_cond_destroy(info_node->u.cond);
- wasm_runtime_free(info_node->u.cond);
- }
- wasm_runtime_free(info_node);
- pthread_mutex_unlock(&pthread_global_lock);
- }
- bool
- lib_pthread_init()
- {
- if (0 != os_mutex_init(&pthread_global_lock))
- return false;
- bh_list_init(&cluster_info_list);
- if (!wasm_cluster_register_destroy_callback(
- lib_pthread_destroy_callback)) {
- os_mutex_destroy(&pthread_global_lock);
- return false;
- }
- return true;
- }
- void
- lib_pthread_destroy()
- {
- os_mutex_destroy(&pthread_global_lock);
- }
- static ClusterInfoNode*
- get_cluster_info(WASMCluster *cluster)
- {
- ClusterInfoNode *node;
- os_mutex_lock(&pthread_global_lock);
- node = bh_list_first_elem(&cluster_info_list);
- while (node) {
- if (cluster == node->cluster) {
- os_mutex_unlock(&pthread_global_lock);
- return node;
- }
- node = bh_list_elem_next(node);
- }
- os_mutex_unlock(&pthread_global_lock);
- return NULL;
- }
- static ClusterInfoNode*
- create_cluster_info(WASMCluster *cluster)
- {
- ClusterInfoNode *node;
- bh_list_status ret;
- if (!(node = wasm_runtime_malloc(sizeof(ClusterInfoNode)))) {
- return NULL;
- }
- node->cluster = cluster;
- if (!(node->thread_info_map =
- bh_hash_map_create(32, true,
- (HashFunc)thread_handle_hash,
- (KeyEqualFunc)thread_handle_equal,
- NULL,
- thread_info_destroy))) {
- wasm_runtime_free(node);
- return NULL;
- }
- os_mutex_lock(&pthread_global_lock);
- ret = bh_list_insert(&cluster_info_list, node);
- bh_assert(ret == 0);
- os_mutex_unlock(&pthread_global_lock);
- (void)ret;
- return node;
- }
- static bool
- destroy_cluster_info(WASMCluster *cluster)
- {
- ClusterInfoNode *node = get_cluster_info(cluster);
- if (node) {
- bh_hash_map_destroy(node->thread_info_map);
- os_mutex_lock(&pthread_global_lock);
- bh_list_remove(&cluster_info_list, node);
- wasm_runtime_free(node);
- os_mutex_unlock(&pthread_global_lock);
- return true;
- }
- return false;
- }
- static void
- lib_pthread_destroy_callback(WASMCluster *cluster)
- {
- destroy_cluster_info(cluster);
- }
- static void
- delete_thread_info_node(ThreadInfoNode *thread_info)
- {
- ClusterInfoNode *node;
- bool ret;
- WASMCluster *cluster =
- wasm_exec_env_get_cluster(thread_info->exec_env);
- if ((node = get_cluster_info(cluster))) {
- ret = bh_hash_map_remove(node->thread_info_map,
- (void *)(uintptr_t)thread_info->handle,
- NULL, NULL);
- (void)ret;
- }
- thread_info_destroy(thread_info);
- }
- static bool
- append_thread_info_node(ThreadInfoNode *thread_info)
- {
- ClusterInfoNode *node;
- WASMCluster *cluster =
- wasm_exec_env_get_cluster(thread_info->exec_env);
- if (!(node = get_cluster_info(cluster))) {
- if (!(node = create_cluster_info(cluster))) {
- return false;
- }
- }
- if (!bh_hash_map_insert(node->thread_info_map,
- (void *)(uintptr_t)thread_info->handle,
- thread_info)) {
- return false;
- }
- return true;
- }
- static ThreadInfoNode*
- get_thread_info(wasm_exec_env_t exec_env, uint32 handle)
- {
- WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
- ClusterInfoNode *info = get_cluster_info(cluster);
- return bh_hash_map_find(info->thread_info_map, (void *)(uintptr_t)handle);
- }
- static uint32
- allocate_handle()
- {
- uint32 id;
- os_mutex_lock(&pthread_global_lock);
- id = handle_id++;
- os_mutex_unlock(&pthread_global_lock);
- return id;
- }
- static void*
- pthread_start_routine(void *arg)
- {
- wasm_exec_env_t exec_env = (wasm_exec_env_t)arg;
- wasm_exec_env_t parent_exec_env;
- wasm_module_inst_t module_inst = get_module_inst(exec_env);
- ThreadRoutineArgs *routine_args = exec_env->thread_arg;
- ThreadInfoNode *info_node = routine_args->info_node;
- uint32 argv[1];
- parent_exec_env = info_node->parent_exec_env;
- os_mutex_lock(&parent_exec_env->wait_lock);
- info_node->exec_env = exec_env;
- info_node->u.thread = exec_env->handle;
- if (!append_thread_info_node(info_node)) {
- wasm_runtime_deinstantiate_internal(module_inst, true);
- delete_thread_info_node(info_node);
- os_cond_signal(&parent_exec_env->wait_cond);
- os_mutex_unlock(&parent_exec_env->wait_lock);
- return NULL;
- }
- info_node->status = THREAD_RUNNING;
- os_cond_signal(&parent_exec_env->wait_cond);
- os_mutex_unlock(&parent_exec_env->wait_lock);
- if (!validate_native_addr(routine_args->arg, sizeof(uint32))) {
- /* If there are exceptions, copy the exception to
- all other instance in this cluster */
- wasm_cluster_spread_exception(exec_env);
- wasm_runtime_deinstantiate_internal(module_inst, true);
- delete_thread_info_node(info_node);
- return NULL;
- }
- argv[0] = addr_native_to_app(routine_args->arg);
- if(!wasm_runtime_call_indirect(exec_env,
- routine_args->elem_index,
- 1, argv)) {
- wasm_cluster_spread_exception(exec_env);
- }
- /* routine exit, destroy instance */
- wasm_runtime_deinstantiate_internal(module_inst, true);
- info_node->status = THREAD_EXIT;
- delete_thread_info_node(info_node);
- return (void *)(uintptr_t)argv[0];
- }
- static int
- pthread_create_wrapper(wasm_exec_env_t exec_env,
- uint32 *thread, /* thread_handle */
- const void *attr, /* not supported */
- uint32 elem_index, /* entry function */
- void *arg) /* arguments buffer */
- {
- wasm_module_t module = get_module(exec_env);
- wasm_module_inst_t new_module_inst = NULL;
- ThreadInfoNode *info_node = NULL;
- ThreadRoutineArgs *routine_args = NULL;
- uint32 thread_handle;
- int32 ret = -1;
- #if WASM_ENABLE_LIBC_WASI != 0
- wasm_module_inst_t module_inst = get_module_inst(exec_env);
- WASIContext *wasi_ctx = get_wasi_ctx(module_inst);
- #endif
- if (!(new_module_inst =
- wasm_runtime_instantiate_internal(module, true, 8192, 0,
- NULL, 0)))
- return -1;
- #if WASM_ENABLE_LIBC_WASI != 0
- if (wasi_ctx)
- wasm_runtime_set_wasi_ctx(new_module_inst, wasi_ctx);
- #endif
- if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode))))
- goto fail;
- memset(info_node, 0, sizeof(ThreadInfoNode));
- thread_handle = allocate_handle();
- info_node->parent_exec_env = exec_env;
- info_node->handle = thread_handle;
- info_node->type = T_THREAD;
- info_node->status = THREAD_INIT;
- if (!(routine_args = wasm_runtime_malloc(sizeof(ThreadRoutineArgs))))
- goto fail;
- routine_args->arg = arg;
- routine_args->elem_index = elem_index;
- routine_args->info_node = info_node;
- routine_args->module_inst = new_module_inst;
- os_mutex_lock(&exec_env->wait_lock);
- ret = wasm_cluster_create_thread(exec_env, new_module_inst,
- pthread_start_routine,
- (void *)routine_args);
- if (ret != 0) {
- os_mutex_unlock(&exec_env->wait_lock);
- goto fail;
- }
- /* Wait for the thread routine to assign the exec_env to
- thread_info_node, otherwise the exec_env in the thread
- info node may be NULL in the next pthread API call */
- os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock);
- os_mutex_unlock(&exec_env->wait_lock);
- if (thread)
- *thread = thread_handle;
- return 0;
- fail:
- if (new_module_inst)
- wasm_runtime_deinstantiate_internal(new_module_inst, true);
- if (info_node)
- wasm_runtime_free(info_node);
- if (routine_args)
- wasm_runtime_free(routine_args);
- return ret;
- }
- static int32
- pthread_join_wrapper(wasm_exec_env_t exec_env, uint32 thread,
- int32 retval_offset) /* void **retval */
- {
- uint32 *ret;
- int32 join_ret;
- void **retval;
- ThreadInfoNode *node;
- wasm_module_inst_t module_inst;
- wasm_exec_env_t target_exec_env;
- node = get_thread_info(exec_env, thread);
- if (!node) {
- /* The thread has exited, return 0 to app */
- return 0;
- }
- target_exec_env = node->exec_env;
- bh_assert(target_exec_env != NULL);
- module_inst = get_module_inst(target_exec_env);
- /* validate addr before join thread, otherwise
- the module_inst may be freed */
- if (!validate_app_addr(retval_offset, sizeof(uint32))) {
- /* Join failed, but we don't want to terminate all threads,
- do not spread exception here */
- wasm_runtime_set_exception(module_inst, NULL);
- return -1;
- }
- retval = (void **)addr_app_to_native(retval_offset);
- join_ret = wasm_cluster_join_thread(target_exec_env, (void **)&ret);
- if (retval_offset != 0)
- *retval = (void*)ret;
- return join_ret;
- }
- static int32
- pthread_detach_wrapper(wasm_exec_env_t exec_env, uint32 thread)
- {
- ThreadInfoNode *node;
- wasm_exec_env_t target_exec_env;
- node = get_thread_info(exec_env, thread);
- if (!node)
- return 0;
- target_exec_env = node->exec_env;
- bh_assert(target_exec_env != NULL);
- return wasm_cluster_detach_thread(target_exec_env);
- }
- static int32
- pthread_cancel_wrapper(wasm_exec_env_t exec_env, uint32 thread)
- {
- ThreadInfoNode *node;
- wasm_exec_env_t target_exec_env;
- node = get_thread_info(exec_env, thread);
- if (!node)
- return 0;
- target_exec_env = node->exec_env;
- bh_assert(target_exec_env != NULL);
- return wasm_cluster_cancel_thread(target_exec_env);
- }
- static int32
- pthread_self_wrapper(wasm_exec_env_t exec_env)
- {
- ThreadRoutineArgs *args = get_thread_arg(exec_env);
- /* If thread_arg is NULL, it's the exec_env of the main thread,
- return id 0 to app */
- if (!args)
- return 0;
- return args->info_node->handle;
- }
- static void
- pthread_exit_wrapper(wasm_exec_env_t exec_env, int32 retval_offset)
- {
- wasm_module_inst_t module_inst = get_module_inst(exec_env);
- ThreadRoutineArgs *args = get_thread_arg(exec_env);
- /* Currently exit main thread is not allowed */
- if (!args)
- return;
- /* routine exit, destroy instance */
- wasm_runtime_deinstantiate_internal(module_inst, true);
- delete_thread_info_node(args->info_node);
- wasm_cluster_exit_thread(exec_env, (void *)(uintptr_t)retval_offset);
- }
- static int32
- pthread_mutex_init_wrapper(wasm_exec_env_t exec_env, uint32 *mutex, void *attr)
- {
- korp_mutex *pmutex;
- ThreadInfoNode *info_node;
- if (!(pmutex = wasm_runtime_malloc(sizeof(korp_mutex)))) {
- return -1;
- }
- if (os_mutex_init(pmutex) != 0) {
- goto fail1;
- }
- if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode))))
- goto fail2;
- memset(info_node, 0, sizeof(ThreadInfoNode));
- info_node->exec_env = exec_env;
- info_node->handle = allocate_handle();
- info_node->type = T_MUTEX;
- info_node->u.mutex = pmutex;
- info_node->status = MUTEX_CREATED;
- if (!append_thread_info_node(info_node))
- goto fail3;
- /* Return the mutex handle to app */
- if (mutex)
- *(uint32*)mutex = info_node->handle;
- return 0;
- fail3:
- delete_thread_info_node(info_node);
- fail2:
- os_mutex_destroy(pmutex);
- fail1:
- wasm_runtime_free(pmutex);
- return -1;
- }
- static int32
- pthread_mutex_lock_wrapper(wasm_exec_env_t exec_env, uint32 *mutex)
- {
- ThreadInfoNode* info_node = get_thread_info(exec_env, *mutex);
- if (!info_node || info_node->type != T_MUTEX)
- return -1;
- return os_mutex_lock(info_node->u.mutex);
- }
- static int32
- pthread_mutex_unlock_wrapper(wasm_exec_env_t exec_env, uint32 *mutex)
- {
- ThreadInfoNode* info_node = get_thread_info(exec_env, *mutex);
- if (!info_node || info_node->type != T_MUTEX)
- return -1;
- return os_mutex_unlock(info_node->u.mutex);
- }
- static int32
- pthread_mutex_destroy_wrapper(wasm_exec_env_t exec_env, uint32 *mutex)
- {
- int32 ret_val;
- ThreadInfoNode* info_node = get_thread_info(exec_env, *mutex);
- if (!info_node || info_node->type != T_MUTEX)
- return -1;
- ret_val = os_mutex_destroy(info_node->u.mutex);
- info_node->status = MUTEX_DESTROYED;
- delete_thread_info_node(info_node);
- return ret_val;
- }
- static int32
- pthread_cond_init_wrapper(wasm_exec_env_t exec_env, uint32 *cond, void *attr)
- {
- korp_cond *pcond;
- ThreadInfoNode *info_node;
- if (!(pcond = wasm_runtime_malloc(sizeof(korp_cond)))) {
- return -1;
- }
- if (os_cond_init(pcond) != 0) {
- goto fail1;
- }
- if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode))))
- goto fail2;
- memset(info_node, 0, sizeof(ThreadInfoNode));
- info_node->exec_env = exec_env;
- info_node->handle = allocate_handle();
- info_node->type = T_COND;
- info_node->u.cond = pcond;
- info_node->status = COND_CREATED;
- if (!append_thread_info_node(info_node))
- goto fail3;
- /* Return the cond handle to app */
- if (cond)
- *(uint32*)cond = info_node->handle;
- return 0;
- fail3:
- delete_thread_info_node(info_node);
- fail2:
- os_cond_destroy(pcond);
- fail1:
- wasm_runtime_free(pcond);
- return -1;
- }
- static int32
- pthread_cond_wait_wrapper(wasm_exec_env_t exec_env, uint32 *cond, uint32 *mutex)
- {
- ThreadInfoNode *cond_info_node, *mutex_info_node;
- cond_info_node = get_thread_info(exec_env, *cond);
- if (!cond_info_node || cond_info_node->type != T_COND)
- return -1;
- mutex_info_node = get_thread_info(exec_env, *mutex);
- if (!mutex_info_node || mutex_info_node->type != T_MUTEX)
- return -1;
- return os_cond_wait(cond_info_node->u.cond, mutex_info_node->u.mutex);
- }
- /* Currently we don't support struct timespec in built-in libc,
- so the pthread_cond_timedwait use useconds instead
- */
- static int32
- pthread_cond_timedwait_wrapper(wasm_exec_env_t exec_env, uint32 *cond,
- uint32 *mutex, uint32 useconds)
- {
- ThreadInfoNode *cond_info_node, *mutex_info_node;
- cond_info_node = get_thread_info(exec_env, *cond);
- if (!cond_info_node || cond_info_node->type != T_COND)
- return -1;
- mutex_info_node = get_thread_info(exec_env, *mutex);
- if (!mutex_info_node || mutex_info_node->type != T_MUTEX)
- return -1;
- return os_cond_reltimedwait(cond_info_node->u.cond,
- mutex_info_node->u.mutex, useconds);
- }
- static int32
- pthread_cond_signal_wrapper(wasm_exec_env_t exec_env, uint32 *cond)
- {
- ThreadInfoNode* info_node = get_thread_info(exec_env, *cond);
- if (!info_node || info_node->type != T_COND)
- return -1;
- return os_cond_signal(info_node->u.cond);
- }
- static int32
- pthread_cond_destroy_wrapper(wasm_exec_env_t exec_env, uint32 *cond)
- {
- int32 ret_val;
- ThreadInfoNode* info_node = get_thread_info(exec_env, *cond);
- if (!info_node || info_node->type != T_COND)
- return -1;
- ret_val = os_cond_destroy(info_node->u.cond);
- info_node->status = COND_DESTROYED;
- delete_thread_info_node(info_node);
- return ret_val;
- }
- #define REG_NATIVE_FUNC(func_name, signature) \
- { #func_name, func_name##_wrapper, signature, NULL }
- static NativeSymbol native_symbols_lib_pthread[] = {
- REG_NATIVE_FUNC(pthread_create, "(**i*)i"),
- REG_NATIVE_FUNC(pthread_join, "(ii)i"),
- REG_NATIVE_FUNC(pthread_detach, "(i)i"),
- REG_NATIVE_FUNC(pthread_cancel, "(i)i"),
- REG_NATIVE_FUNC(pthread_self, "()i"),
- REG_NATIVE_FUNC(pthread_exit, "(i)"),
- REG_NATIVE_FUNC(pthread_mutex_init, "(**)i"),
- REG_NATIVE_FUNC(pthread_mutex_lock, "(*)i"),
- REG_NATIVE_FUNC(pthread_mutex_unlock, "(*)i"),
- REG_NATIVE_FUNC(pthread_mutex_destroy, "(*)i"),
- REG_NATIVE_FUNC(pthread_cond_init, "(**)i"),
- REG_NATIVE_FUNC(pthread_cond_wait, "(**)i"),
- REG_NATIVE_FUNC(pthread_cond_timedwait, "(**i)i"),
- REG_NATIVE_FUNC(pthread_cond_signal, "(*)i"),
- REG_NATIVE_FUNC(pthread_cond_destroy, "(*)i"),
- };
- uint32
- get_lib_pthread_export_apis(NativeSymbol **p_lib_pthread_apis)
- {
- *p_lib_pthread_apis = native_symbols_lib_pthread;
- return sizeof(native_symbols_lib_pthread) / sizeof(NativeSymbol);
- }
|