diff --git a/core/iwasm/common/wasm_exec_env.c b/core/iwasm/common/wasm_exec_env.c index 622bcd71e..5f317944d 100644 --- a/core/iwasm/common/wasm_exec_env.c +++ b/core/iwasm/common/wasm_exec_env.c @@ -49,17 +49,12 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, if (os_cond_init(&exec_env->wait_cond) != 0) goto fail3; - -#if WASM_ENABLE_DEBUG_INTERP != 0 - if (!(exec_env->current_status = wasm_cluster_create_exenv_status())) - goto fail4; -#endif #endif #ifdef OS_ENABLE_HW_BOUND_CHECK if (!(exec_env->exce_check_guard_page = os_mmap(NULL, os_getpagesize(), MMAP_PROT_NONE, MMAP_MAP_NONE))) - goto fail5; + goto fail4; #endif exec_env->module_inst = module_inst; @@ -83,16 +78,10 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, return exec_env; #ifdef OS_ENABLE_HW_BOUND_CHECK -fail5: -#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0 - wasm_cluster_destroy_exenv_status(exec_env->current_status); -#endif +fail4: #endif #if WASM_ENABLE_THREAD_MGR != 0 -#if WASM_ENABLE_DEBUG_INTERP != 0 -fail4: os_cond_destroy(&exec_env->wait_cond); -#endif fail3: os_mutex_destroy(&exec_env->wait_lock); fail2: @@ -114,9 +103,6 @@ wasm_exec_env_destroy_internal(WASMExecEnv *exec_env) #if WASM_ENABLE_THREAD_MGR != 0 os_mutex_destroy(&exec_env->wait_lock); os_cond_destroy(&exec_env->wait_cond); -#if WASM_ENABLE_DEBUG_INTERP != 0 - wasm_cluster_destroy_exenv_status(exec_env->current_status); -#endif #endif #if WASM_ENABLE_AOT != 0 wasm_runtime_free(exec_env->argv_buf); diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h index bf9892080..ac090f5f6 100644 --- a/core/iwasm/common/wasm_exec_env.h +++ b/core/iwasm/common/wasm_exec_env.h @@ -21,9 +21,6 @@ struct WASMInterpFrame; #if WASM_ENABLE_THREAD_MGR != 0 typedef struct WASMCluster WASMCluster; -#if WASM_ENABLE_DEBUG_INTERP != 0 -typedef struct WASMCurrentEnvStatus WASMCurrentEnvStatus; -#endif #endif #ifdef OS_ENABLE_HW_BOUND_CHECK @@ -33,6 +30,22 @@ typedef struct WASMJmpBuf { } WASMJmpBuf; #endif +typedef enum ThreadRunningState { + WASM_THREAD_RUNNING = 0, + WASM_THREAD_VMWAIT = 1, + WASM_THREAD_SUSPENDED = 2, + WASM_THREAD_EXITED = 3, + + WASM_THREAD_STOP = 4, + WASM_THREAD_STEP = 5, +} ThreadRunningState; + +typedef struct WASMThreadStatus { + uint64 signal_flag : 32; + uint64 step_count : 16; + uint64 running_state : 16; +} WASMThreadStatus; + /* Execution environment */ typedef struct WASMExecEnv { /* Next thread's exec env of a WASM module instance. */ @@ -106,20 +119,25 @@ typedef struct WASMExecEnv { /* pointer to the cluster */ WASMCluster *cluster; - /* used to support debugger */ + /* the lock for protecting wait_cond, wait_count and + thread_is_detached of this exec_env */ korp_mutex wait_lock; + + /* conditional variable for this thread to wait */ korp_cond wait_cond; + /* the count of threads which are joining current thread */ uint32 wait_count; + /* the count of threads which are suspending current thread */ + uint32 suspend_count; + + WASMThreadStatus current_status; + /* whether current thread is detached */ bool thread_is_detached; #endif -#if WASM_ENABLE_DEBUG_INTERP != 0 - WASMCurrentEnvStatus *current_status; -#endif - /* attachment for native function */ void *attachment; diff --git a/core/iwasm/common/wasm_memory.c b/core/iwasm/common/wasm_memory.c index 05635ef51..b104299ca 100644 --- a/core/iwasm/common/wasm_memory.c +++ b/core/iwasm/common/wasm_memory.c @@ -702,19 +702,9 @@ wasm_enlarge_memory_internal(WASMModuleInstance *module, uint32 inc_page_count) return_func: if (!ret && enlarge_memory_error_cb) { - WASMExecEnv *exec_env = NULL; - -#if WASM_ENABLE_INTERP != 0 - if (module->module_type == Wasm_Module_Bytecode) - exec_env = - ((WASMModuleInstanceExtra *)module->e)->common.cur_exec_env; -#endif -#if WASM_ENABLE_AOT != 0 - if (module->module_type == Wasm_Module_AoT) - exec_env = - ((AOTModuleInstanceExtra *)module->e)->common.cur_exec_env; -#endif - + WASMExecEnv *exec_env = + wasm_runtime_get_cur_exec_env((WASMModuleInstanceCommon *)module); + bh_assert(exec_env); enlarge_memory_error_cb(inc_page_count, total_size_old, 0, failure_reason, (WASMModuleInstanceCommon *)module, exec_env, @@ -809,19 +799,9 @@ wasm_enlarge_memory_internal(WASMModuleInstance *module, uint32 inc_page_count) return_func: if (!ret && enlarge_memory_error_cb) { - WASMExecEnv *exec_env = NULL; - -#if WASM_ENABLE_INTERP != 0 - if (module->module_type == Wasm_Module_Bytecode) - exec_env = - ((WASMModuleInstanceExtra *)module->e)->common.cur_exec_env; -#endif -#if WASM_ENABLE_AOT != 0 - if (module->module_type == Wasm_Module_AoT) - exec_env = - ((AOTModuleInstanceExtra *)module->e)->common.cur_exec_env; -#endif - + WASMExecEnv *exec_env = + wasm_runtime_get_cur_exec_env((WASMModuleInstanceCommon *)module); + bh_assert(exec_env); enlarge_memory_error_cb(inc_page_count, total_size_old, 0, failure_reason, (WASMModuleInstanceCommon *)module, exec_env, diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 8faa5e444..019f99769 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -1195,6 +1195,18 @@ wasm_runtime_set_max_thread_num(uint32 num) { wasm_cluster_set_max_thread_num(num); } + +void +wasm_runtime_enter_safe_state() +{ + wasm_cluster_change_curr_thread_to_safe(); +} + +void +wasm_runtime_exit_safe_state() +{ + wasm_cluster_change_curr_thread_to_running(); +} #endif /* end of WASM_ENABLE_THREAD_MGR */ static WASMModuleCommon * @@ -1707,6 +1719,29 @@ wasm_runtime_get_user_data(WASMExecEnv *exec_env) return exec_env->user_data; } +WASMExecEnv * +wasm_runtime_get_cur_exec_env(const WASMModuleInstanceCommon *module_inst_comm) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + WASMExecEnv *cur_exec_env = NULL; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + cur_exec_env = + ((WASMModuleInstanceExtra *)module_inst->e)->common.cur_exec_env; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + cur_exec_env = + ((AOTModuleInstanceExtra *)module_inst->e)->common.cur_exec_env; +#endif + + return cur_exec_env; +} + #ifdef OS_ENABLE_HW_BOUND_CHECK void wasm_runtime_access_exce_check_guard_page() diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index a834d67f2..a9518564a 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -594,6 +594,10 @@ wasm_runtime_set_user_data(WASMExecEnv *exec_env, void *user_data); WASM_RUNTIME_API_EXTERN void * wasm_runtime_get_user_data(WASMExecEnv *exec_env); +/* Get the exec_env currently used by the module instance */ +WASMExecEnv * +wasm_runtime_get_cur_exec_env(const WASMModuleInstanceCommon *module_inst); + #if WASM_CONFIGUABLE_BOUNDS_CHECKS != 0 /* See wasm_export.h for description */ WASM_RUNTIME_API_EXTERN void @@ -845,6 +849,12 @@ wasm_exec_env_get_aux_stack(WASMExecEnv *exec_env, uint32 *start_offset, bool wasm_exec_env_set_aux_stack(WASMExecEnv *exec_env, uint32 start_offset, uint32 size); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_enter_safe_state(); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_exit_safe_state(); #endif #if WASM_ENABLE_LIBC_WASI != 0 diff --git a/core/iwasm/common/wasm_shared_memory.c b/core/iwasm/common/wasm_shared_memory.c index c95d4b7ed..5c06a3465 100644 --- a/core/iwasm/common/wasm_shared_memory.c +++ b/core/iwasm/common/wasm_shared_memory.c @@ -310,7 +310,7 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address, #if WASM_ENABLE_THREAD_MGR != 0 exec_env = - wasm_clusters_search_exec_env((WASMModuleInstanceCommon *)module_inst); + wasm_runtime_get_cur_exec_env((WASMModuleInstanceCommon *)module_inst); bh_assert(exec_env); #endif diff --git a/core/iwasm/compilation/aot_emit_control.c b/core/iwasm/compilation/aot_emit_control.c index 446ca5ea6..5216955bc 100644 --- a/core/iwasm/compilation/aot_emit_control.c +++ b/core/iwasm/compilation/aot_emit_control.c @@ -718,6 +718,7 @@ check_suspend_flags(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) will always be loaded from memory rather than register */ LLVMSetVolatile(terminate_flags, true); + /* WASM_SUSPEND_FLAG_TERMINATE is 1 */ if (!(flag = LLVMBuildAnd(comp_ctx->builder, terminate_flags, I32_ONE, "termination_flag"))) { aot_set_last_error("llvm build AND failed"); diff --git a/core/iwasm/fast-jit/fe/jit_emit_control.c b/core/iwasm/fast-jit/fe/jit_emit_control.c index f7536c73e..eda6554b7 100644 --- a/core/iwasm/fast-jit/fe/jit_emit_control.c +++ b/core/iwasm/fast-jit/fe/jit_emit_control.c @@ -925,6 +925,7 @@ jit_check_suspend_flags(JitCompContext *cc) offset = jit_cc_new_const_I32(cc, offsetof(WASMExecEnv, suspend_flags)); GEN_INSN(LDI32, suspend_flags, exec_env, offset); + /* WASM_SUSPEND_FLAG_TERMINATE is 1 */ GEN_INSN(AND, terminate_flag, suspend_flags, NEW_CONST(I32, 1)); GEN_INSN(CMP, cc->cmp_reg, terminate_flag, NEW_CONST(I32, 0)); diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 50860d3a3..72e422077 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -13,8 +13,10 @@ #if WASM_ENABLE_SHARED_MEMORY != 0 #include "../common/wasm_shared_memory.h" #endif -#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0 +#if WASM_ENABLE_THREAD_MGR != 0 #include "../libraries/thread-mgr/thread_manager.h" +#endif +#if WASM_ENABLE_DEBUG_INTERP != 0 #include "../libraries/debug-engine/debug_engine.h" #endif #if WASM_ENABLE_FAST_JIT != 0 @@ -1049,47 +1051,42 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, #if WASM_ENABLE_THREAD_MGR != 0 #if WASM_ENABLE_DEBUG_INTERP != 0 -#define CHECK_SUSPEND_FLAGS() \ - do { \ - os_mutex_lock(&exec_env->wait_lock); \ - if (IS_WAMR_TERM_SIG(exec_env->current_status->signal_flag)) { \ - os_mutex_unlock(&exec_env->wait_lock); \ - return; \ - } \ - if (IS_WAMR_STOP_SIG(exec_env->current_status->signal_flag)) { \ - SYNC_ALL_TO_FRAME(); \ - wasm_cluster_thread_waiting_run(exec_env); \ - } \ - os_mutex_unlock(&exec_env->wait_lock); \ - } while (0) -#else -#if WASM_SUSPEND_FLAGS_IS_ATOMIC != 0 -/* The lock is only needed when the suspend_flags is atomic; otherwise - the lock is already taken at the time when SUSPENSION_LOCK() is called. */ -#define SUSPENSION_LOCK() os_mutex_lock(&exec_env->wait_lock); -#define SUSPENSION_UNLOCK() os_mutex_unlock(&exec_env->wait_lock); -#else -#define SUSPENSION_LOCK() -#define SUSPENSION_UNLOCK() -#endif - #define CHECK_SUSPEND_FLAGS() \ do { \ - WASM_SUSPEND_FLAGS_LOCK(exec_env->wait_lock); \ - if (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) \ - & WASM_SUSPEND_FLAG_TERMINATE) { \ - /* terminate current thread */ \ - WASM_SUSPEND_FLAGS_UNLOCK(exec_env->wait_lock); \ + os_mutex_lock(&exec_env->wait_lock); \ + if (IS_WAMR_TERM_SIG(exec_env->current_status.signal_flag)) { \ + os_mutex_unlock(&exec_env->wait_lock); \ return; \ } \ - while (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) \ - & WASM_SUSPEND_FLAG_SUSPEND) { \ - /* suspend current thread */ \ - SUSPENSION_LOCK() \ - os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock); \ - SUSPENSION_UNLOCK() \ + if (IS_WAMR_STOP_SIG(exec_env->current_status.signal_flag)) { \ + SYNC_ALL_TO_FRAME(); \ + wasm_cluster_thread_waiting_run(exec_env); \ } \ - WASM_SUSPEND_FLAGS_UNLOCK(exec_env->wait_lock); \ + os_mutex_unlock(&exec_env->wait_lock); \ + } while (0) +#else +#define CHECK_SUSPEND_FLAGS() \ + do { \ + uint32 suspend_flags, suspend_count; \ + WASM_SUSPEND_FLAGS_LOCK(exec_env->cluster->thread_state_lock); \ + suspend_flags = WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags); \ + if (suspend_flags != 0) { \ + suspend_count = exec_env->suspend_count; \ + WASM_SUSPEND_FLAGS_UNLOCK(exec_env->cluster->thread_state_lock); \ + if (suspend_flags & WASM_SUSPEND_FLAG_TERMINATE) { \ + /* terminate current thread */ \ + return; \ + } \ + if (suspend_count > 0) { \ + SYNC_ALL_TO_FRAME(); \ + wasm_thread_change_to_running(exec_env); \ + if (wasm_copy_exception(module, NULL)) \ + goto got_exception; \ + } \ + } \ + else { \ + WASM_SUSPEND_FLAGS_UNLOCK(exec_env->cluster->thread_state_lock); \ + } \ } while (0) #endif /* WASM_ENABLE_DEBUG_INTERP */ #endif /* WASM_ENABLE_THREAD_MGR */ @@ -1106,9 +1103,9 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, debugger can know the exact opcode who caused the exception */ \ frame_ip_orig = frame_ip; \ os_mutex_lock(&exec_env->wait_lock); \ - while (exec_env->current_status->signal_flag == WAMR_SIG_SINGSTEP \ - && exec_env->current_status->step_count++ == 1) { \ - exec_env->current_status->step_count = 0; \ + while (exec_env->current_status.signal_flag == WAMR_SIG_SINGSTEP \ + && exec_env->current_status.step_count++ == 1) { \ + exec_env->current_status.step_count = 0; \ SYNC_ALL_TO_FRAME(); \ wasm_cluster_thread_waiting_run(exec_env); \ } \ @@ -1122,15 +1119,15 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, #else /* else of WASM_ENABLE_LABELS_AS_VALUES */ #define HANDLE_OP(opcode) case opcode: #if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0 -#define HANDLE_OP_END() \ - os_mutex_lock(&exec_env->wait_lock); \ - if (exec_env->current_status->signal_flag == WAMR_SIG_SINGSTEP \ - && exec_env->current_status->step_count++ == 2) { \ - exec_env->current_status->step_count = 0; \ - SYNC_ALL_TO_FRAME(); \ - wasm_cluster_thread_waiting_run(exec_env); \ - } \ - os_mutex_unlock(&exec_env->wait_lock); \ +#define HANDLE_OP_END() \ + os_mutex_lock(&exec_env->wait_lock); \ + if (exec_env->current_status.signal_flag == WAMR_SIG_SINGSTEP \ + && exec_env->current_status.step_count++ == 2) { \ + exec_env->current_status.step_count = 0; \ + SYNC_ALL_TO_FRAME(); \ + wasm_cluster_thread_waiting_run(exec_env); \ + } \ + os_mutex_unlock(&exec_env->wait_lock); \ continue #else #define HANDLE_OP_END() continue diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index e565c2c89..415d8950b 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -13,6 +13,9 @@ #if WASM_ENABLE_SHARED_MEMORY != 0 #include "../common/wasm_shared_memory.h" #endif +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#endif typedef int32 CellType_I32; typedef int64 CellType_I64; @@ -1066,17 +1069,28 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, #endif #if WASM_ENABLE_THREAD_MGR != 0 -#define CHECK_SUSPEND_FLAGS() \ - do { \ - WASM_SUSPEND_FLAGS_LOCK(exec_env->wait_lock); \ - if (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) \ - & WASM_SUSPEND_FLAG_TERMINATE) { \ - /* terminate current thread */ \ - WASM_SUSPEND_FLAGS_UNLOCK(exec_env->wait_lock); \ - return; \ - } \ - /* TODO: support suspend and breakpoint */ \ - WASM_SUSPEND_FLAGS_UNLOCK(exec_env->wait_lock); \ +#define CHECK_SUSPEND_FLAGS() \ + do { \ + uint32 suspend_flags, suspend_count; \ + WASM_SUSPEND_FLAGS_LOCK(exec_env->cluster->thread_state_lock); \ + suspend_flags = WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags); \ + if (suspend_flags != 0) { \ + suspend_count = exec_env->suspend_count; \ + WASM_SUSPEND_FLAGS_UNLOCK(exec_env->cluster->thread_state_lock); \ + if (suspend_flags & WASM_SUSPEND_FLAG_TERMINATE) { \ + /* terminate current thread */ \ + return; \ + } \ + if (suspend_count > 0) { \ + SYNC_ALL_TO_FRAME(); \ + wasm_thread_change_to_running(exec_env); \ + if (wasm_copy_exception(module, NULL)) \ + goto got_exception; \ + } \ + } \ + else { \ + WASM_SUSPEND_FLAGS_UNLOCK(exec_env->cluster->thread_state_lock); \ + } \ } while (0) #endif diff --git a/core/iwasm/libraries/debug-engine/debug_engine.c b/core/iwasm/libraries/debug-engine/debug_engine.c index 1b3db1d49..1d5968d31 100644 --- a/core/iwasm/libraries/debug-engine/debug_engine.c +++ b/core/iwasm/libraries/debug-engine/debug_engine.c @@ -166,12 +166,11 @@ control_thread_routine(void *arg) korp_tid tid; status = (uint32)debug_inst->stopped_thread->current_status - ->signal_flag; + .signal_flag; tid = debug_inst->stopped_thread->handle; - if (debug_inst->stopped_thread->current_status - ->running_status - == STATUS_EXIT) { + if (debug_inst->stopped_thread->current_status.running_state + == WASM_THREAD_EXITED) { /* If the thread exits, report "W00" if it's the last * thread in the cluster, otherwise ignore this event */ status = 0; @@ -602,7 +601,7 @@ wasm_debug_instance_get_thread_status(WASMDebugInstance *instance, korp_tid tid) exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); while (exec_env) { if (exec_env->handle == tid) { - return (uint32)exec_env->current_status->signal_flag; + return (uint32)exec_env->current_status.signal_flag; } exec_env = bh_list_elem_next(exec_env); } @@ -1105,7 +1104,7 @@ wasm_debug_instance_on_failure(WASMDebugInstance *instance) /* Resume all threads so they can receive the TERM signal */ os_mutex_lock(&exec_env->wait_lock); wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM); - exec_env->current_status->running_status = STATUS_RUNNING; + exec_env->current_status.running_state = WASM_THREAD_RUNNING; os_cond_signal(&exec_env->wait_cond); os_mutex_unlock(&exec_env->wait_lock); exec_env = bh_list_elem_next(exec_env); @@ -1208,7 +1207,7 @@ wasm_debug_instance_kill(WASMDebugInstance *instance) if (instance->current_state == APP_STOPPED) { /* Resume all threads so they can receive the TERM signal */ os_mutex_lock(&exec_env->wait_lock); - exec_env->current_status->running_status = STATUS_RUNNING; + exec_env->current_status.running_state = WASM_THREAD_RUNNING; os_cond_signal(&exec_env->wait_cond); os_mutex_unlock(&exec_env->wait_lock); } diff --git a/core/iwasm/libraries/debug-engine/handler.c b/core/iwasm/libraries/debug-engine/handler.c index 8d451b1a3..08daec520 100644 --- a/core/iwasm/libraries/debug-engine/handler.c +++ b/core/iwasm/libraries/debug-engine/handler.c @@ -526,7 +526,7 @@ handle_threadstop_request(WASMGDBServer *server, char *payload) os_mutex_unlock(&debug_inst->wait_lock); tid = debug_inst->stopped_thread->handle; - status = (uint32)debug_inst->stopped_thread->current_status->signal_flag; + status = (uint32)debug_inst->stopped_thread->current_status.signal_flag; wasm_debug_instance_set_cur_thread(debug_inst, tid); diff --git a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c index 56deaff32..0ef00fb53 100644 --- a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c +++ b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c @@ -674,7 +674,8 @@ pthread_join_wrapper(wasm_exec_env_t exec_env, uint32 thread, if (node->status != THREAD_EXIT) { /* if the thread is still running, call the platforms join API */ - join_ret = wasm_cluster_join_thread(target_exec_env, (void **)&ret); + join_ret = + wasm_cluster_join_thread(target_exec_env, exec_env, (void **)&ret); } else { /* if the thread has exited, return stored results */ diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c index 49545de72..ac00b5124 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.c +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -21,6 +21,11 @@ typedef struct { void (*destroy_cb)(WASMCluster *); } DestroyCallBackNode; +typedef struct GlobalExecEnvNode { + struct GlobalExecEnvNode *next; + WASMExecEnv *exec_env; +} GlobalExecEnvNode; + static bh_list destroy_callback_list_head; static bh_list *const destroy_callback_list = &destroy_callback_list_head; @@ -43,18 +48,198 @@ wasm_cluster_set_max_thread_num(uint32 num) cluster_max_thread_num = num; } +static korp_mutex global_exec_env_list_lock; +static GlobalExecEnvNode *global_exec_env_list = NULL; + +static bool +global_exec_env_list_add(WASMExecEnv *exec_env) +{ + GlobalExecEnvNode *exec_env_node; + + exec_env_node = wasm_runtime_malloc(sizeof(GlobalExecEnvNode)); + if (!exec_env_node) { + return false; + } + + os_mutex_lock(&global_exec_env_list_lock); + + exec_env_node->exec_env = exec_env; + exec_env_node->next = global_exec_env_list; + global_exec_env_list = exec_env_node; + + os_mutex_unlock(&global_exec_env_list_lock); + + return true; +} + +static void +global_exec_env_list_del(WASMExecEnv *exec_env) +{ + GlobalExecEnvNode *exec_env_node, *prev = NULL; + + os_mutex_lock(&global_exec_env_list_lock); + + exec_env_node = global_exec_env_list; + + while (exec_env_node) { + if (exec_env_node->exec_env == exec_env) { + if (prev) + prev->next = exec_env_node->next; + else + global_exec_env_list = exec_env_node->next; + wasm_runtime_free(exec_env_node); + break; + } + prev = exec_env_node; + exec_env_node = exec_env_node->next; + } + + os_mutex_unlock(&global_exec_env_list_lock); +} + +static void +global_exec_env_list_del_exec_envs(WASMCluster *cluster) +{ + GlobalExecEnvNode *exec_env_node, *prev = NULL, *next; + + os_mutex_lock(&global_exec_env_list_lock); + + exec_env_node = global_exec_env_list; + + while (exec_env_node) { + if (exec_env_node->exec_env->cluster == cluster) { + if (prev) + prev->next = next = exec_env_node->next; + else + global_exec_env_list = next = exec_env_node->next; + + wasm_runtime_free(exec_env_node); + exec_env_node = next; + } + else { + prev = exec_env_node; + exec_env_node = exec_env_node->next; + } + } + + os_mutex_unlock(&global_exec_env_list_lock); +} + +static WASMExecEnv * +global_exec_env_list_find_with_inst(WASMModuleInstanceCommon *module_inst) +{ + GlobalExecEnvNode *exec_env_node; + WASMExecEnv *exec_env; + + os_mutex_lock(&global_exec_env_list_lock); + + exec_env_node = global_exec_env_list; + + while (exec_env_node) { + if (exec_env_node->exec_env->module_inst == module_inst) { + exec_env = exec_env_node->exec_env; + os_mutex_unlock(&global_exec_env_list_lock); + return exec_env; + } + exec_env_node = exec_env_node->next; + } + + os_mutex_unlock(&global_exec_env_list_lock); + + return NULL; +} + +static WASMExecEnv * +global_exec_env_list_find_with_tid(korp_tid handle) +{ + GlobalExecEnvNode *exec_env_node; + WASMExecEnv *exec_env; + + os_mutex_lock(&global_exec_env_list_lock); + + exec_env_node = global_exec_env_list; + + while (exec_env_node) { + if (exec_env_node->exec_env->handle == handle) { + exec_env = exec_env_node->exec_env; + os_mutex_unlock(&global_exec_env_list_lock); + return exec_env; + } + exec_env_node = exec_env_node->next; + } + + os_mutex_unlock(&global_exec_env_list_lock); + + return NULL; +} + +static WASMExecEnv * +get_exec_env_of_current_thread() +{ + korp_tid handle = os_self_thread(); + return global_exec_env_list_find_with_tid(handle); +} + +static bool +global_exec_env_list_has_exec_env(WASMExecEnv *exec_env) +{ + GlobalExecEnvNode *exec_env_node; + + os_mutex_lock(&global_exec_env_list_lock); + + exec_env_node = global_exec_env_list; + + while (exec_env_node) { + if (exec_env_node->exec_env == exec_env) { + os_mutex_unlock(&global_exec_env_list_lock); + return true; + } + exec_env_node = exec_env_node->next; + } + + os_mutex_unlock(&global_exec_env_list_lock); + + return false; +} + +static void +global_exec_env_list_destroy() +{ + GlobalExecEnvNode *exec_env_node, *next; + + /* Destroy global_exec_env_list */ + exec_env_node = global_exec_env_list; + while (exec_env_node) { + next = exec_env_node->next; + wasm_runtime_free(exec_env_node); + exec_env_node = next; + } + + global_exec_env_list = NULL; +} + bool thread_manager_init() { if (bh_list_init(cluster_list) != 0) return false; + if (os_mutex_init(&cluster_list_lock) != 0) return false; - if (os_mutex_init(&_exception_lock) != 0) { - os_mutex_destroy(&cluster_list_lock); - return false; - } + + if (os_mutex_init(&_exception_lock) != 0) + goto fail1; + + if (os_mutex_init(&global_exec_env_list_lock) != 0) + goto fail2; + return true; + +fail2: + os_mutex_destroy(&_exception_lock); +fail1: + os_mutex_destroy(&cluster_list_lock); + return false; } void @@ -62,16 +247,136 @@ thread_manager_destroy() { WASMCluster *cluster = bh_list_first_elem(cluster_list); WASMCluster *next; + while (cluster) { next = bh_list_elem_next(cluster); wasm_cluster_destroy(cluster); cluster = next; } wasm_cluster_cancel_all_callbacks(); + + global_exec_env_list_destroy(); + os_mutex_destroy(&global_exec_env_list_lock); + os_mutex_destroy(&_exception_lock); os_mutex_destroy(&cluster_list_lock); } +/* Safely change us to RUNNING state with pending suspensions + checking, i.e. current state (-> SUSPENDED) -> RUNNING. */ +ThreadRunningState +wasm_thread_change_to_running(WASMExecEnv *self) +{ + WASMCluster *cluster = self->cluster; + ThreadRunningState old_state; + + os_mutex_lock(&cluster->thread_state_lock); + + old_state = self->current_status.running_state; + + /* Suspend us while there are pending requests from other threads */ + if (self->suspend_count > 0) { + self->current_status.running_state = WASM_THREAD_SUSPENDED; + + os_cond_broadcast(&cluster->thread_safe_cond); + + do { + os_cond_wait(&cluster->thread_resume_cond, + &cluster->thread_state_lock); + } while (self->suspend_count > 0); + } + + self->current_status.running_state = WASM_THREAD_RUNNING; + + os_mutex_unlock(&cluster->thread_state_lock); + + return old_state; +} + +/* Change us to a safe state and notify threads that are waiting for + this condition (through thread_safed_cond) */ +static ThreadRunningState +wasm_thread_change_to_safe(WASMExecEnv *self, ThreadRunningState state) +{ + WASMCluster *cluster = self->cluster; + ThreadRunningState old_state; + + /* This lock can act as a memory barrier to ensure all results have + been committed to memory before chaning our state to a safe + state. */ + os_mutex_lock(&cluster->thread_state_lock); + + bh_assert(state != WASM_THREAD_RUNNING); + old_state = self->current_status.running_state; + self->current_status.running_state = state; + + os_cond_broadcast(&cluster->thread_safe_cond); + + os_mutex_unlock(&cluster->thread_state_lock); + + return old_state; +} + +static void +cluster_lock_thread_list(WASMCluster *cluster, WASMExecEnv *self) +{ + if (self) { + bh_assert(self->current_status.running_state == WASM_THREAD_RUNNING); + } + + /* If we are a thread of cluster, we must avoid dead lock between us + and another thread of cluster who is suspending all or resuming all, + which also requires the cluster lock. */ + for (;;) { + if (os_mutex_trylock(&cluster->lock)) { + /* The trylock failed, go the slow path to get the lock. */ + + /* The lock may be held by a thread that is suspending us, + so we must change to a safe state before grabbing the + lock with blocking approach. */ + if (self) + wasm_thread_change_to_safe(self, WASM_THREAD_VMWAIT); + + /* Grab the lock with blocking approach since we have been + in a safe state. */ + os_mutex_lock(&cluster->lock); + } + + if (!self) { + break; + } + + /* Now, we've got the lock, if there are pending suspensions, + we must release the lock, suspend us and retry to grab the + lock because we may have been suspended by a thread that + has suspended all threads, which needs this lock to do + resume-all. If we suspend us when return to RUNNING state + while holding the lock, deadlock may occur. We don't need + to lock the thread_state_lock because our suspend_count can + only be increased by a thread holding thread_list_lock. + Decreasing this count in this period (can only be done by + wasm_thread_resume) is safe since it may just make us loop + again. */ + if (self->suspend_count == 0) { + /* We are not suspended, so changing back to RUNNING state + with holding the thread list lock is safe. */ + self->current_status.running_state = WASM_THREAD_RUNNING; + break; + } + else { + /* Unlock the thread list lock, suspend us and retry. */ + os_mutex_unlock(&cluster->lock); + wasm_thread_change_to_running(self); + } + } +} + +static inline void +cluster_unlock_thread_list(WASMCluster *cluster) +{ + os_mutex_unlock(&cluster->lock); +} + static void traverse_list(bh_list *l, list_visitor visitor, void *user_data) { @@ -85,8 +390,8 @@ traverse_list(bh_list *l, list_visitor visitor, void *user_data) /* Assumes cluster->lock is locked */ static bool -safe_traverse_exec_env_list(WASMCluster *cluster, list_visitor visitor, - void *user_data) +safe_traverse_exec_env_list(WASMCluster *cluster, WASMExecEnv *self, + list_visitor visitor, void *user_data) { Vector proc_nodes; void *node; @@ -119,9 +424,9 @@ safe_traverse_exec_env_list(WASMCluster *cluster, list_visitor visitor, continue; } - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); visitor(node, user_data); - os_mutex_lock(&cluster->lock); + cluster_lock_thread_list(cluster, self); if (!bh_vector_append(&proc_nodes, &node)) { ret = false; goto final; @@ -213,6 +518,7 @@ wasm_cluster_create(WASMExecEnv *exec_env) { WASMCluster *cluster; uint32 aux_stack_start, aux_stack_size; + bh_list_status ret; bh_assert(exec_env->cluster == NULL); if (!(cluster = wasm_runtime_malloc(sizeof(WASMCluster)))) { @@ -224,11 +530,31 @@ wasm_cluster_create(WASMExecEnv *exec_env) exec_env->cluster = cluster; bh_list_init(&cluster->exec_env_list); - bh_list_insert(&cluster->exec_env_list, exec_env); + ret = bh_list_insert(&cluster->exec_env_list, exec_env); + bh_assert(ret == BH_LIST_SUCCESS); + (void)ret; + + if (!global_exec_env_list_add(exec_env)) + goto fail1; + if (os_mutex_init(&cluster->lock) != 0) { - wasm_runtime_free(cluster); - LOG_ERROR("thread manager error: failed to init mutex"); - return NULL; + LOG_ERROR("thread manager error: failed to init cluster lock"); + goto fail2; + } + + if (os_mutex_init(&cluster->thread_state_lock) != 0) { + LOG_ERROR("thread manager error: failed to init thread_state_lock"); + goto fail3; + } + + if (os_cond_init(&cluster->thread_safe_cond) != 0) { + LOG_ERROR("thread manager error: failed to init thread_safe_cond"); + goto fail4; + } + + if (os_cond_init(&cluster->thread_resume_cond) != 0) { + LOG_ERROR("thread manager error: failed to init thread_resume_cond"); + goto fail5; } /* Prepare the aux stack top and size for every thread */ @@ -241,10 +567,9 @@ wasm_cluster_create(WASMExecEnv *exec_env) /* If the module don't have aux stack info, don't throw error here, but remain stack_tops and stack_segment_occupied as NULL */ os_mutex_lock(&cluster_list_lock); - if (bh_list_insert(cluster_list, cluster) != 0) { - os_mutex_unlock(&cluster_list_lock); - goto fail; - } + ret = bh_list_insert(cluster_list, cluster); + bh_assert(ret == BH_LIST_SUCCESS); + (void)ret; os_mutex_unlock(&cluster_list_lock); return cluster; @@ -255,7 +580,7 @@ wasm_cluster_create(WASMExecEnv *exec_env) #else cluster->stack_size = aux_stack_size / (cluster_max_thread_num + 1); if (cluster->stack_size < WASM_THREAD_AUX_STACK_SIZE_MIN) { - goto fail; + goto fail6; } /* Make stack size 16-byte aligned */ cluster->stack_size = cluster->stack_size & (~15); @@ -265,7 +590,7 @@ wasm_cluster_create(WASMExecEnv *exec_env) aux stack boundary to the main exec_env */ if (!wasm_exec_env_set_aux_stack(exec_env, aux_stack_start, cluster->stack_size)) - goto fail; + goto fail6; #if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 if (cluster_max_thread_num != 0) { @@ -274,13 +599,14 @@ wasm_cluster_create(WASMExecEnv *exec_env) if (total_size >= UINT32_MAX || !(cluster->stack_tops = wasm_runtime_malloc((uint32)total_size))) { - goto fail; + goto fail6; } memset(cluster->stack_tops, 0, (uint32)total_size); if (!(cluster->stack_segment_occupied = wasm_runtime_malloc(cluster_max_thread_num * sizeof(bool)))) { - goto fail; + wasm_runtime_free(cluster->stack_tops); + goto fail6; } memset(cluster->stack_segment_occupied, 0, cluster_max_thread_num * sizeof(bool)); @@ -295,17 +621,25 @@ wasm_cluster_create(WASMExecEnv *exec_env) #endif os_mutex_lock(&cluster_list_lock); - if (bh_list_insert(cluster_list, cluster) != 0) { - os_mutex_unlock(&cluster_list_lock); - goto fail; - } + ret = bh_list_insert(cluster_list, cluster); + bh_assert(ret == BH_LIST_SUCCESS); + (void)ret; os_mutex_unlock(&cluster_list_lock); return cluster; -fail: - if (cluster) - wasm_cluster_destroy(cluster); +fail6: + os_cond_destroy(&cluster->thread_resume_cond); +fail5: + os_cond_destroy(&cluster->thread_safe_cond); +fail4: + os_mutex_destroy(&cluster->thread_state_lock); +fail3: + os_mutex_destroy(&cluster->lock); +fail2: + global_exec_env_list_del(exec_env); +fail1: + wasm_runtime_free(cluster); return NULL; } @@ -325,11 +659,17 @@ wasm_cluster_destroy(WASMCluster *cluster) traverse_list(destroy_callback_list, destroy_cluster_visitor, (void *)cluster); + /* Destroy exec_envs of this cluster from global_exec_env_list */ + global_exec_env_list_del_exec_envs(cluster); + /* Remove the cluster from the cluster list */ os_mutex_lock(&cluster_list_lock); bh_list_remove(cluster_list, cluster); os_mutex_unlock(&cluster_list_lock); + os_cond_destroy(&cluster->thread_resume_cond); + os_cond_destroy(&cluster->thread_safe_cond); + os_mutex_destroy(&cluster->thread_state_lock); os_mutex_destroy(&cluster->lock); #if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 @@ -369,27 +709,32 @@ wasm_exec_env_get_cluster(WASMExecEnv *exec_env) static bool wasm_cluster_add_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env) { - bool ret = true; + bh_list_status ret; exec_env->cluster = cluster; if (cluster->exec_env_list.len == cluster_max_thread_num + 1) { LOG_ERROR("thread manager error: " "maximum number of threads exceeded"); - ret = false; + return false; } - if (ret && bh_list_insert(&cluster->exec_env_list, exec_env) != 0) - ret = false; + if (!global_exec_env_list_add(exec_env)) { + return false; + } - return ret; + ret = bh_list_insert(&cluster->exec_env_list, exec_env); + bh_assert(ret == BH_LIST_SUCCESS); + (void)ret; + + return true; } -static bool +/* The caller should lock cluster->lock for thread safety */ +static void wasm_cluster_del_exec_env_internal(WASMCluster *cluster, WASMExecEnv *exec_env, bool can_destroy_cluster) { - bool ret = true; bh_assert(exec_env->cluster == cluster); #if WASM_ENABLE_DEBUG_INTERP != 0 @@ -407,8 +752,10 @@ wasm_cluster_del_exec_env_internal(WASMCluster *cluster, WASMExecEnv *exec_env, os_mutex_unlock(&cluster->debug_inst->wait_lock); } #endif - if (bh_list_remove(&cluster->exec_env_list, exec_env) != 0) - ret = false; + + global_exec_env_list_del(exec_env); + + bh_list_remove(&cluster->exec_env_list, exec_env); if (can_destroy_cluster) { if (cluster->exec_env_list.len == 0) { @@ -419,58 +766,21 @@ wasm_cluster_del_exec_env_internal(WASMCluster *cluster, WASMExecEnv *exec_env, else { /* Don't destroy cluster as cluster->lock is being used */ } - - return ret; } /* The caller should lock cluster->lock for thread safety */ -bool +void wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env) { - return wasm_cluster_del_exec_env_internal(cluster, exec_env, true); + wasm_cluster_del_exec_env_internal(cluster, exec_env, true); } -static WASMExecEnv * -wasm_cluster_search_exec_env(WASMCluster *cluster, - WASMModuleInstanceCommon *module_inst) -{ - WASMExecEnv *node = NULL; - - os_mutex_lock(&cluster->lock); - node = bh_list_first_elem(&cluster->exec_env_list); - while (node) { - if (node->module_inst == module_inst) { - os_mutex_unlock(&cluster->lock); - return node; - } - node = bh_list_elem_next(node); - } - - os_mutex_unlock(&cluster->lock); - return NULL; -} - -/* search the global cluster list to find if the given - module instance have a corresponding exec_env */ +/* Search the global exec_env list to find if the given + module instance has a corresponding exec_env */ WASMExecEnv * wasm_clusters_search_exec_env(WASMModuleInstanceCommon *module_inst) { - WASMCluster *cluster = NULL; - WASMExecEnv *exec_env = NULL; - - os_mutex_lock(&cluster_list_lock); - cluster = bh_list_first_elem(cluster_list); - while (cluster) { - exec_env = wasm_cluster_search_exec_env(cluster, module_inst); - if (exec_env) { - os_mutex_unlock(&cluster_list_lock); - return exec_env; - } - cluster = bh_list_elem_next(cluster); - } - - os_mutex_unlock(&cluster_list_lock); - return NULL; + return global_exec_env_list_find_with_inst(module_inst); } WASMExecEnv * @@ -488,7 +798,7 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env) return NULL; } - os_mutex_lock(&cluster->lock); + cluster_lock_thread_list(cluster, exec_env); if (cluster->has_exception || cluster->processing) { goto fail1; @@ -536,25 +846,26 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env) goto fail4; } - /* Inherit suspend_flags of parent thread */ + /* Inherit suspend_flags of parent thread, no need to acquire + thread_state_lock as the thread list has been locked */ new_exec_env->suspend_flags.flags = exec_env->suspend_flags.flags; if (!wasm_cluster_add_exec_env(cluster, new_exec_env)) goto fail4; - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); return new_exec_env; fail4: - /* free the allocated aux stack space */ + /* Free the allocated aux stack space */ free_aux_stack(exec_env, aux_stack_start); fail3: wasm_exec_env_destroy_internal(new_exec_env); fail2: wasm_runtime_deinstantiate_internal(new_module_inst, true); fail1: - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); return NULL; } @@ -564,9 +875,11 @@ wasm_cluster_destroy_spawned_exec_env(WASMExecEnv *exec_env) { WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + WASMExecEnv *self = get_exec_env_of_current_thread(); + bh_assert(cluster != NULL); - os_mutex_lock(&cluster->lock); + cluster_lock_thread_list(cluster, self); /* Free aux stack space */ free_aux_stack(exec_env, exec_env->aux_stack_bottom.bottom); @@ -577,7 +890,7 @@ wasm_cluster_destroy_spawned_exec_env(WASMExecEnv *exec_env) /* Routine exit, destroy instance */ wasm_runtime_deinstantiate_internal(module_inst, true); - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); } /* start routine of thread manager */ @@ -599,26 +912,29 @@ thread_manager_start_routine(void *arg) os_cond_signal(&exec_env->wait_cond); os_mutex_unlock(&exec_env->wait_lock); + /* Check the pending suspensions */ + wasm_thread_change_to_running(exec_env); + ret = exec_env->thread_start_routine(exec_env); #ifdef OS_ENABLE_HW_BOUND_CHECK - os_mutex_lock(&exec_env->wait_lock); - if (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) - & WASM_SUSPEND_FLAG_EXIT) + os_mutex_lock(&cluster->thread_state_lock); + if (exec_env->suspend_flags.flags & WASM_SUSPEND_FLAG_EXIT) ret = exec_env->thread_ret_value; - os_mutex_unlock(&exec_env->wait_lock); + os_mutex_unlock(&cluster->thread_state_lock); #endif /* Routine exit */ + cluster_lock_thread_list(cluster, exec_env); + + exec_env->current_status.running_state = WASM_THREAD_EXITED; + #if WASM_ENABLE_DEBUG_INTERP != 0 wasm_cluster_thread_exited(exec_env); #endif - os_mutex_lock(&cluster_list_lock); - - os_mutex_lock(&cluster->lock); - + os_mutex_lock(&exec_env->wait_lock); /* Detach the native thread here to ensure the resources are freed */ if (exec_env->wait_count == 0 && !exec_env->thread_is_detached) { /* Only detach current thread when there is no other thread @@ -628,6 +944,7 @@ thread_manager_start_routine(void *arg) /* No need to set exec_env->thread_is_detached to true here since we will exit soon */ } + os_mutex_unlock(&exec_env->wait_lock); /* Free aux stack space */ free_aux_stack(exec_env, exec_env->aux_stack_bottom.bottom); @@ -638,9 +955,7 @@ thread_manager_start_routine(void *arg) /* Routine exit, destroy instance */ wasm_runtime_deinstantiate_internal(module_inst, true); - os_mutex_unlock(&cluster->lock); - - os_mutex_unlock(&cluster_list_lock); + cluster_unlock_thread_list(cluster); os_thread_exit(ret); return ret; @@ -659,7 +974,7 @@ wasm_cluster_create_thread(WASMExecEnv *exec_env, cluster = wasm_exec_env_get_cluster(exec_env); bh_assert(cluster); - os_mutex_lock(&cluster->lock); + cluster_lock_thread_list(cluster, exec_env); if (cluster->has_exception || cluster->processing) { goto fail1; @@ -689,7 +1004,8 @@ wasm_cluster_create_thread(WASMExecEnv *exec_env, new_exec_env->aux_stack_bottom.bottom = UINT32_MAX; } - /* Inherit suspend_flags of parent thread */ + /* Inherit suspend_flags of parent thread, no need to acquire + thread_state_lock as the thread list has been locked */ new_exec_env->suspend_flags.flags = exec_env->suspend_flags.flags; if (!wasm_cluster_add_exec_env(cluster, new_exec_env)) @@ -713,20 +1029,20 @@ wasm_cluster_create_thread(WASMExecEnv *exec_env, os_cond_wait(&new_exec_env->wait_cond, &new_exec_env->wait_lock); os_mutex_unlock(&new_exec_env->wait_lock); - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); return 0; fail4: wasm_cluster_del_exec_env_internal(cluster, new_exec_env, false); fail3: - /* free the allocated aux stack space */ + /* Free the allocated aux stack space */ if (alloc_aux_stack) free_aux_stack(exec_env, aux_stack_start); fail2: wasm_exec_env_destroy_internal(new_exec_env); fail1: - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); return -1; } @@ -781,44 +1097,23 @@ wasm_cluster_dup_c_api_imports(WASMModuleInstanceCommon *module_inst_dst, } #if WASM_ENABLE_DEBUG_INTERP != 0 -WASMCurrentEnvStatus * -wasm_cluster_create_exenv_status() -{ - WASMCurrentEnvStatus *status; - - if (!(status = wasm_runtime_malloc(sizeof(WASMCurrentEnvStatus)))) { - return NULL; - } - - status->step_count = 0; - status->signal_flag = 0; - status->running_status = 0; - return status; -} - -void -wasm_cluster_destroy_exenv_status(WASMCurrentEnvStatus *status) -{ - wasm_runtime_free(status); -} - inline static bool wasm_cluster_thread_is_running(WASMExecEnv *exec_env) { - return exec_env->current_status->running_status == STATUS_RUNNING - || exec_env->current_status->running_status == STATUS_STEP; + return exec_env->current_status.running_state == WASM_THREAD_RUNNING + || exec_env->current_status.running_state == WASM_THREAD_STEP; } void wasm_cluster_clear_thread_signal(WASMExecEnv *exec_env) { - exec_env->current_status->signal_flag = 0; + exec_env->current_status.signal_flag = 0; } void wasm_cluster_thread_send_signal(WASMExecEnv *exec_env, uint32 signo) { - exec_env->current_status->signal_flag = signo; + exec_env->current_status.signal_flag = signo; } static void @@ -854,7 +1149,7 @@ notify_debug_instance_exit(WASMExecEnv *exec_env) void wasm_cluster_thread_waiting_run(WASMExecEnv *exec_env) { - exec_env->current_status->running_status = STATUS_STOP; + exec_env->current_status.running_state = WASM_THREAD_STOP; notify_debug_instance(exec_env); while (!wasm_cluster_thread_is_running(exec_env)) { @@ -875,7 +1170,8 @@ wasm_cluster_send_signal_all(WASMCluster *cluster, uint32 signo) void wasm_cluster_thread_exited(WASMExecEnv *exec_env) { - exec_env->current_status->running_status = STATUS_EXIT; + /* TODO */ + exec_env->current_status.running_state = WASM_THREAD_EXITED; notify_debug_instance_exit(exec_env); } @@ -884,7 +1180,8 @@ wasm_cluster_thread_continue(WASMExecEnv *exec_env) { os_mutex_lock(&exec_env->wait_lock); wasm_cluster_clear_thread_signal(exec_env); - exec_env->current_status->running_status = STATUS_RUNNING; + /* TODO */ + exec_env->current_status.running_state = WASM_THREAD_RUNNING; os_cond_signal(&exec_env->wait_cond); os_mutex_unlock(&exec_env->wait_lock); } @@ -893,7 +1190,8 @@ void wasm_cluster_thread_step(WASMExecEnv *exec_env) { os_mutex_lock(&exec_env->wait_lock); - exec_env->current_status->running_status = STATUS_STEP; + /* TODO */ + exec_env->current_status.running_state = WASM_THREAD_STEP; os_cond_signal(&exec_env->wait_cond); os_mutex_unlock(&exec_env->wait_lock); } @@ -903,60 +1201,53 @@ wasm_cluster_set_debug_inst(WASMCluster *cluster, WASMDebugInstance *inst) { cluster->debug_inst = inst; } - #endif /* end of WASM_ENABLE_DEBUG_INTERP */ -/* Check whether the exec_env is in one of all clusters, the caller - should add lock to the cluster list before calling us */ +/* Check whether the exec_env is in one of all clusters */ static bool clusters_have_exec_env(WASMExecEnv *exec_env) { - WASMCluster *cluster = bh_list_first_elem(cluster_list); - WASMExecEnv *node; - - while (cluster) { - os_mutex_lock(&cluster->lock); - node = bh_list_first_elem(&cluster->exec_env_list); - - while (node) { - if (node == exec_env) { - bh_assert(exec_env->cluster == cluster); - os_mutex_unlock(&cluster->lock); - return true; - } - node = bh_list_elem_next(node); - } - os_mutex_unlock(&cluster->lock); - - cluster = bh_list_elem_next(cluster); - } - - return false; + return global_exec_env_list_has_exec_env(exec_env); } int32 -wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val) +wasm_cluster_join_thread(WASMExecEnv *exec_env, WASMExecEnv *self, + void **ret_val) { korp_tid handle; + int32 ret; - os_mutex_lock(&cluster_list_lock); - - if (!clusters_have_exec_env(exec_env) || exec_env->thread_is_detached) { - /* Invalid thread, thread has exited or thread has been detached */ + if (!clusters_have_exec_env(exec_env)) { + /* Invalid thread or thread has exited */ if (ret_val) *ret_val = NULL; - os_mutex_unlock(&cluster_list_lock); return 0; } os_mutex_lock(&exec_env->wait_lock); + + if (exec_env->thread_is_detached) { + /* Thread has been detached */ + if (ret_val) + *ret_val = NULL; + os_mutex_unlock(&exec_env->wait_lock); + return 0; + } + exec_env->wait_count++; handle = exec_env->handle; + os_mutex_unlock(&exec_env->wait_lock); - os_mutex_unlock(&cluster_list_lock); + if (self) + wasm_thread_change_to_safe(self, WASM_THREAD_VMWAIT); - return os_thread_join(handle, ret_val); + ret = os_thread_join(handle, ret_val); + + if (self) + wasm_thread_change_to_running(self); + + return ret; } int32 @@ -964,20 +1255,25 @@ wasm_cluster_detach_thread(WASMExecEnv *exec_env) { int32 ret = 0; - os_mutex_lock(&cluster_list_lock); if (!clusters_have_exec_env(exec_env)) { /* Invalid thread or the thread has exited */ - os_mutex_unlock(&cluster_list_lock); return 0; } + + os_mutex_lock(&exec_env->wait_lock); if (exec_env->wait_count == 0 && !exec_env->thread_is_detached) { /* Only detach current thread when there is no other thread joining it, otherwise let the system resources for the thread be released after joining */ ret = os_thread_detach(exec_env->handle); exec_env->thread_is_detached = true; + + os_mutex_lock(&exec_env->cluster->thread_state_lock); + exec_env->current_status.running_state = WASM_THREAD_EXITED; + os_mutex_unlock(&exec_env->cluster->thread_state_lock); } - os_mutex_unlock(&cluster_list_lock); + os_mutex_unlock(&exec_env->wait_lock); + return ret; } @@ -987,13 +1283,16 @@ wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval) WASMCluster *cluster; WASMModuleInstanceCommon *module_inst; + cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + #ifdef OS_ENABLE_HW_BOUND_CHECK if (exec_env->jmpbuf_stack_top) { + os_mutex_lock(&cluster->thread_state_lock); /* Store the return value in exec_env */ exec_env->thread_ret_value = retval; - - WASM_SUSPEND_FLAGS_FETCH_OR(exec_env->suspend_flags, - WASM_SUSPEND_FLAG_EXIT); + exec_env->suspend_flags.flags |= WASM_SUSPEND_FLAG_EXIT; + os_mutex_unlock(&cluster->thread_state_lock); #ifndef BH_PLATFORM_WINDOWS /* Pop all jmpbuf_node except the last one */ @@ -1006,19 +1305,18 @@ wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval) } #endif - cluster = wasm_exec_env_get_cluster(exec_env); - bh_assert(cluster); + /* App exit the thread, free the resources before exit native thread */ + + cluster_lock_thread_list(cluster, exec_env); + + exec_env->current_status.running_state = WASM_THREAD_EXITED; + #if WASM_ENABLE_DEBUG_INTERP != 0 wasm_cluster_clear_thread_signal(exec_env); wasm_cluster_thread_exited(exec_env); #endif - /* App exit the thread, free the resources before exit native thread */ - - os_mutex_lock(&cluster_list_lock); - - os_mutex_lock(&cluster->lock); - + os_mutex_lock(&exec_env->wait_lock); /* Detach the native thread here to ensure the resources are freed */ if (exec_env->wait_count == 0 && !exec_env->thread_is_detached) { /* Only detach current thread when there is no other thread @@ -1028,6 +1326,7 @@ wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval) /* No need to set exec_env->thread_is_detached to true here since we will exit soon */ } + os_mutex_unlock(&exec_env->wait_lock); module_inst = exec_env->module_inst; @@ -1040,9 +1339,7 @@ wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval) /* Routine exit, destroy instance */ wasm_runtime_deinstantiate_internal(module_inst, true); - os_mutex_unlock(&cluster->lock); - - os_mutex_unlock(&cluster_list_lock); + cluster_unlock_thread_list(cluster); os_thread_exit(retval); } @@ -1050,15 +1347,16 @@ wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval) static void set_thread_cancel_flags(WASMExecEnv *exec_env) { - os_mutex_lock(&exec_env->wait_lock); + bh_assert(exec_env->cluster); + + os_mutex_lock(&exec_env->cluster->thread_state_lock); #if WASM_ENABLE_DEBUG_INTERP != 0 wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM); #endif - WASM_SUSPEND_FLAGS_FETCH_OR(exec_env->suspend_flags, - WASM_SUSPEND_FLAG_TERMINATE); + exec_env->suspend_flags.flags |= WASM_SUSPEND_FLAG_TERMINATE; - os_mutex_unlock(&exec_env->wait_lock); + os_mutex_unlock(&exec_env->cluster->thread_state_lock); #ifdef OS_ENABLE_WAKEUP_BLOCKING_OP wasm_runtime_interrupt_blocking_op(exec_env); @@ -1068,32 +1366,25 @@ set_thread_cancel_flags(WASMExecEnv *exec_env) static void clear_thread_cancel_flags(WASMExecEnv *exec_env) { - os_mutex_lock(&exec_env->wait_lock); - WASM_SUSPEND_FLAGS_FETCH_AND(exec_env->suspend_flags, - ~WASM_SUSPEND_FLAG_TERMINATE); - os_mutex_unlock(&exec_env->wait_lock); + bh_assert(exec_env->cluster); + + os_mutex_lock(&exec_env->cluster->thread_state_lock); + + exec_env->suspend_flags.flags &= ~WASM_SUSPEND_FLAG_TERMINATE; + + os_mutex_unlock(&exec_env->cluster->thread_state_lock); } int32 wasm_cluster_cancel_thread(WASMExecEnv *exec_env) { - os_mutex_lock(&cluster_list_lock); - - if (!exec_env->cluster) { - os_mutex_unlock(&cluster_list_lock); + if (!clusters_have_exec_env(exec_env) || !exec_env->cluster) { + /* Invalid thread or the thread has exited */ return 0; } - if (!clusters_have_exec_env(exec_env)) { - /* Invalid thread or the thread has exited */ - goto final; - } - set_thread_cancel_flags(exec_env); -final: - os_mutex_unlock(&cluster_list_lock); - return 0; } @@ -1107,33 +1398,34 @@ terminate_thread_visitor(void *node, void *user_data) return; wasm_cluster_cancel_thread(curr_exec_env); - wasm_cluster_join_thread(curr_exec_env, NULL); + wasm_cluster_join_thread(curr_exec_env, exec_env, NULL); } void wasm_cluster_terminate_all(WASMCluster *cluster) { - os_mutex_lock(&cluster->lock); + WASMExecEnv *self = get_exec_env_of_current_thread(); + + cluster_lock_thread_list(cluster, self); cluster->processing = true; - safe_traverse_exec_env_list(cluster, terminate_thread_visitor, NULL); + safe_traverse_exec_env_list(cluster, self, terminate_thread_visitor, self); cluster->processing = false; - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); } void -wasm_cluster_terminate_all_except_self(WASMCluster *cluster, - WASMExecEnv *exec_env) +wasm_cluster_terminate_all_except_self(WASMCluster *cluster, WASMExecEnv *self) { - os_mutex_lock(&cluster->lock); + cluster_lock_thread_list(cluster, self); cluster->processing = true; - safe_traverse_exec_env_list(cluster, terminate_thread_visitor, - (void *)exec_env); + safe_traverse_exec_env_list(cluster, self, terminate_thread_visitor, + (void *)self); cluster->processing = false; - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); } static void @@ -1145,109 +1437,189 @@ wait_for_thread_visitor(void *node, void *user_data) if (curr_exec_env == exec_env) return; - wasm_cluster_join_thread(curr_exec_env, NULL); + wasm_cluster_join_thread(curr_exec_env, exec_env, NULL); } void wams_cluster_wait_for_all(WASMCluster *cluster) { - os_mutex_lock(&cluster->lock); + WASMExecEnv *self = get_exec_env_of_current_thread(); + + cluster_lock_thread_list(cluster, self); cluster->processing = true; - safe_traverse_exec_env_list(cluster, wait_for_thread_visitor, NULL); + safe_traverse_exec_env_list(cluster, self, wait_for_thread_visitor, self); cluster->processing = false; - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); } void -wasm_cluster_wait_for_all_except_self(WASMCluster *cluster, - WASMExecEnv *exec_env) +wasm_cluster_wait_for_all_except_self(WASMCluster *cluster, WASMExecEnv *self) { - os_mutex_lock(&cluster->lock); + cluster_lock_thread_list(cluster, self); cluster->processing = true; - safe_traverse_exec_env_list(cluster, wait_for_thread_visitor, - (void *)exec_env); + safe_traverse_exec_env_list(cluster, self, wait_for_thread_visitor, + (void *)self); cluster->processing = false; - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); } bool wasm_cluster_register_destroy_callback(void (*callback)(WASMCluster *)) { DestroyCallBackNode *node; + bh_list_status ret; if (!(node = wasm_runtime_malloc(sizeof(DestroyCallBackNode)))) { LOG_ERROR("thread manager error: failed to allocate memory"); return false; } + node->destroy_cb = callback; - bh_list_insert(destroy_callback_list, node); + ret = bh_list_insert(destroy_callback_list, node); + bh_assert(ret == BH_LIST_SUCCESS); + (void)ret; + return true; } -void -wasm_cluster_suspend_thread(WASMExecEnv *exec_env) +/* Wait for the given thread, which may not be us, to suspend at a + safe point, i.e. to get to a safe state. */ +static void +wait_for_thread_suspend(WASMExecEnv *exec_env) { - /* Set the suspend flag */ - WASM_SUSPEND_FLAGS_FETCH_OR(exec_env->suspend_flags, - WASM_SUSPEND_FLAG_SUSPEND); + WASMCluster *cluster = exec_env->cluster; + + os_mutex_lock(&cluster->thread_state_lock); + + while (exec_env->current_status.running_state == WASM_THREAD_RUNNING) + os_cond_wait(&cluster->thread_safe_cond, &cluster->thread_state_lock); + + os_mutex_unlock(&cluster->thread_state_lock); } -static void -suspend_thread_visitor(void *node, void *user_data) +void +wasm_cluster_suspend_thread(WASMExecEnv *exec_env, WASMExecEnv *self) { - WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; - WASMExecEnv *exec_env = (WASMExecEnv *)user_data; + WASMCluster *cluster = exec_env->cluster; - if (curr_exec_env == exec_env) - return; + /* Hold cluster lock until the target thread is suspended. */ + cluster_lock_thread_list(cluster, self); - wasm_cluster_suspend_thread(curr_exec_env); + os_mutex_lock(&cluster->thread_state_lock); + exec_env->suspend_count++; + /* Set the suspend flag */ + exec_env->suspend_flags.flags |= WASM_SUSPEND_FLAG_SUSPEND; + os_mutex_unlock(&cluster->thread_state_lock); + + wait_for_thread_suspend(exec_env); + + /* Don't release cluster lock until the target thread is + suspended successfully. Otherwise, it may suspend us and + cause deadlock. */ + cluster_unlock_thread_list(cluster); +} + +void +wasm_cluster_suspend_all_except_self(WASMCluster *cluster, WASMExecEnv *self) +{ + WASMExecEnv *exec_env; + + /* Hold thread_list_lock until target threads are suspended */ + cluster_lock_thread_list(cluster, self); + + os_mutex_lock(&cluster->thread_state_lock); + + /* Increase suspend count for all threads except us */ + exec_env = bh_list_first_elem(&cluster->exec_env_list); + while (exec_env) { + if (exec_env != self) { + exec_env->suspend_count++; + exec_env->suspend_flags.flags |= WASM_SUSPEND_FLAG_SUSPEND; + } + exec_env = bh_list_elem_next(exec_env); + } + + os_mutex_unlock(&cluster->thread_state_lock); + + exec_env = bh_list_first_elem(&cluster->exec_env_list); + while (exec_env) { + if (exec_env != self) + wait_for_thread_suspend(exec_env); + exec_env = bh_list_elem_next(exec_env); + } + + /* Hold thread_list_lock until target threads are suspended */ + cluster_unlock_thread_list(cluster); } void wasm_cluster_suspend_all(WASMCluster *cluster) { - os_mutex_lock(&cluster->lock); - traverse_list(&cluster->exec_env_list, suspend_thread_visitor, NULL); - os_mutex_unlock(&cluster->lock); -} + WASMExecEnv *self = get_exec_env_of_current_thread(); -void -wasm_cluster_suspend_all_except_self(WASMCluster *cluster, - WASMExecEnv *exec_env) -{ - os_mutex_lock(&cluster->lock); - traverse_list(&cluster->exec_env_list, suspend_thread_visitor, - (void *)exec_env); - os_mutex_unlock(&cluster->lock); + wasm_cluster_suspend_all_except_self(cluster, self); } void wasm_cluster_resume_thread(WASMExecEnv *exec_env) { - WASM_SUSPEND_FLAGS_FETCH_AND(exec_env->suspend_flags, - ~WASM_SUSPEND_FLAG_SUSPEND); - os_cond_signal(&exec_env->wait_cond); + WASMCluster *cluster = exec_env->cluster; + + os_mutex_lock(&cluster->thread_state_lock); + + if (exec_env->suspend_count > 0) { + exec_env->suspend_count--; + + if (exec_env->suspend_count == 0) { + exec_env->suspend_flags.flags &= ~WASM_SUSPEND_FLAG_SUSPEND; + os_cond_broadcast(&cluster->thread_resume_cond); + } + } + + os_mutex_unlock(&cluster->thread_state_lock); } -static void -resume_thread_visitor(void *node, void *user_data) +void +wasm_cluster_resume_all_except_self(WASMCluster *cluster, WASMExecEnv *self) { - WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMExecEnv *exec_env; - wasm_cluster_resume_thread(curr_exec_env); + cluster_lock_thread_list(cluster, self); + + /* No need to acquire the thread_state_lock + as the thread list has been locked */ + + /* Decrease suspend count for all threads except us. */ + exec_env = bh_list_first_elem(&cluster->exec_env_list); + while (exec_env) { + if (exec_env != self) { + exec_env->suspend_count--; + + if (exec_env->suspend_count == 0) { + exec_env->suspend_flags.flags &= ~WASM_SUSPEND_FLAG_SUSPEND; + os_cond_broadcast(&cluster->thread_resume_cond); + } + } + exec_env = bh_list_elem_next(exec_env); + } + + cluster_unlock_thread_list(cluster); + + os_mutex_lock(&cluster->thread_state_lock); + os_cond_broadcast(&cluster->thread_resume_cond); + os_mutex_unlock(&cluster->thread_state_lock); } void wasm_cluster_resume_all(WASMCluster *cluster) { - os_mutex_lock(&cluster->lock); - traverse_list(&cluster->exec_env_list, resume_thread_visitor, NULL); - os_mutex_unlock(&cluster->lock); + WASMExecEnv *self = get_exec_env_of_current_thread(); + + wasm_cluster_resume_all_except_self(cluster, self); } struct spread_exception_data { @@ -1296,10 +1668,10 @@ wasm_cluster_set_exception(WASMExecEnv *exec_env, const char *exception) data.skip = NULL; data.exception = exception; - os_mutex_lock(&cluster->lock); + cluster_lock_thread_list(cluster, exec_env); cluster->has_exception = has_exception; traverse_list(&cluster->exec_env_list, set_exception_visitor, &data); - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); } static void @@ -1327,10 +1699,10 @@ wasm_cluster_spread_custom_data(WASMModuleInstanceCommon *module_inst, cluster = wasm_exec_env_get_cluster(exec_env); bh_assert(cluster); - os_mutex_lock(&cluster->lock); + cluster_lock_thread_list(cluster, exec_env); traverse_list(&cluster->exec_env_list, set_custom_data_visitor, custom_data); - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); } } @@ -1369,9 +1741,9 @@ wasm_cluster_set_context(WASMModuleInstanceCommon *module_inst, void *key, cluster = wasm_exec_env_get_cluster(exec_env); bh_assert(cluster); - os_mutex_lock(&cluster->lock); + cluster_lock_thread_list(cluster, exec_env); traverse_list(&cluster->exec_env_list, set_context_visitor, &data); - os_mutex_unlock(&cluster->lock); + cluster_unlock_thread_list(cluster); } } #endif /* WASM_ENABLE_MODULE_INST_CONTEXT != 0 */ @@ -1379,16 +1751,39 @@ wasm_cluster_set_context(WASMModuleInstanceCommon *module_inst, void *key, bool wasm_cluster_is_thread_terminated(WASMExecEnv *exec_env) { - os_mutex_lock(&exec_env->wait_lock); - bool is_thread_terminated = (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) - & WASM_SUSPEND_FLAG_TERMINATE) - ? true - : false; - os_mutex_unlock(&exec_env->wait_lock); + bool is_thread_terminated; + + if (!clusters_have_exec_env(exec_env)) { + return true; + } + + os_mutex_lock(&exec_env->cluster->thread_state_lock); + is_thread_terminated = + (exec_env->suspend_flags.flags & WASM_SUSPEND_FLAG_TERMINATE) ? true + : false; + os_mutex_unlock(&exec_env->cluster->thread_state_lock); return is_thread_terminated; } +void +wasm_cluster_change_curr_thread_to_running() +{ + WASMExecEnv *self = get_exec_env_of_current_thread(); + + bh_assert(self); + wasm_thread_change_to_running(self); +} + +void +wasm_cluster_change_curr_thread_to_safe() +{ + WASMExecEnv *self = get_exec_env_of_current_thread(); + + bh_assert(self); + wasm_thread_change_to_safe(self, WASM_THREAD_VMWAIT); +} + void exception_lock(WASMModuleInstance *module_inst) { diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.h b/core/iwasm/libraries/thread-mgr/thread_manager.h index 19186f597..68d8915b9 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.h +++ b/core/iwasm/libraries/thread-mgr/thread_manager.h @@ -23,9 +23,33 @@ typedef struct WASMDebugInstance WASMDebugInstance; struct WASMCluster { struct WASMCluster *next; + /* The lock for exec_env_list, has_exception and processing */ korp_mutex lock; + bh_list exec_env_list; + /* The lock for thread_safe_cond, thread_resume_cond and the below + fields of exec_env: + suspend_flags, suspend_count, current_status, thread_ret_value */ + korp_mutex thread_state_lock; + + /* Condition variable for notifying threads that are waiting for + threads to go to a safe state. All threads waiting for this + condition share this one condition varialbe, so when waken up, + they must check whether the waited threads are really in a safe + state. It's protected by thread_state_lock since it involves + operations of thread states. */ + korp_cond thread_safe_cond; + + /* Condition variable for notifying threads that are waiting for + resumption that their suspend counts have changed and they may be + able to resume (i.e. change to RUNNING state). All suspended + threads share this one condition variable, so when waken up, they + must recheck their suspend counts to check whether they are + really able to resume. It's protected by thread_state_lock since + it involves operations of suspend counts. */ + korp_cond thread_resume_cond; + #if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 /* The aux stack of a module with shared memory will be divided into several segments. This array store the @@ -34,13 +58,16 @@ struct WASMCluster { /* Record which segments are occupied */ bool *stack_segment_occupied; #endif + /* Size of every stack segment */ uint32 stack_size; + /* When has_exception == true, this cluster should refuse any spawn thread * requests, this flag can be cleared by calling * wasm_runtime_clear_exception on instances of any threads of this cluster */ bool has_exception; + /* When processing is true, this cluster should refuse any spawn thread * requests. This is a short-lived state, must be cleared immediately once * the processing finished. @@ -48,6 +75,7 @@ struct WASMCluster { * with lock, see wams_cluster_wait_for_all and wasm_cluster_terminate_all */ bool processing; + #if WASM_ENABLE_DEBUG_INTERP != 0 WASMDebugInstance *debug_inst; #endif @@ -85,7 +113,8 @@ wasm_cluster_create_thread(WASMExecEnv *exec_env, void *(*thread_routine)(void *), void *arg); int32 -wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val); +wasm_cluster_join_thread(WASMExecEnv *exec_env, WASMExecEnv *self, + void **ret_val); int32 wasm_cluster_detach_thread(WASMExecEnv *exec_env); @@ -103,18 +132,21 @@ void wasm_cluster_cancel_all_callbacks(); void -wasm_cluster_suspend_all(WASMCluster *cluster); +wasm_cluster_suspend_thread(WASMExecEnv *exec_env, WASMExecEnv *self); void wasm_cluster_suspend_all_except_self(WASMCluster *cluster, WASMExecEnv *exec_env); void -wasm_cluster_suspend_thread(WASMExecEnv *exec_env); +wasm_cluster_suspend_all(WASMCluster *cluster); void wasm_cluster_resume_thread(WASMExecEnv *exec_env); +void +wasm_cluster_resume_all_except_self(WASMCluster *cluster, WASMExecEnv *self); + void wasm_cluster_resume_all(WASMCluster *cluster); @@ -132,7 +164,7 @@ void wasm_cluster_wait_for_all_except_self(WASMCluster *cluster, WASMExecEnv *exec_env); -bool +void wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env); WASMExecEnv * @@ -158,34 +190,27 @@ wasm_cluster_set_context(WASMModuleInstanceCommon *module_inst, void *key, bool wasm_cluster_is_thread_terminated(WASMExecEnv *exec_env); +ThreadRunningState +wasm_thread_change_to_running(WASMExecEnv *self); + +void +wasm_cluster_change_curr_thread_to_running(); + +void +wasm_cluster_change_curr_thread_to_safe(); + #if WASM_ENABLE_DEBUG_INTERP != 0 + #define WAMR_SIG_TRAP (5) #define WAMR_SIG_STOP (19) #define WAMR_SIG_TERM (15) #define WAMR_SIG_SINGSTEP (0x1ff) -#define STATUS_RUNNING (0) -#define STATUS_STOP (1) -#define STATUS_EXIT (2) -#define STATUS_STEP (3) - #define IS_WAMR_TERM_SIG(signo) ((signo) == WAMR_SIG_TERM) #define IS_WAMR_STOP_SIG(signo) \ ((signo) == WAMR_SIG_STOP || (signo) == WAMR_SIG_TRAP) -struct WASMCurrentEnvStatus { - uint64 signal_flag : 32; - uint64 step_count : 16; - uint64 running_status : 16; -}; - -WASMCurrentEnvStatus * -wasm_cluster_create_exenv_status(); - -void -wasm_cluster_destroy_exenv_status(WASMCurrentEnvStatus *status); - void wasm_cluster_send_signal_all(WASMCluster *cluster, uint32 signo); diff --git a/core/shared/platform/alios/alios_thread.c b/core/shared/platform/alios/alios_thread.c index 9fe927db0..b75455d39 100644 --- a/core/shared/platform/alios/alios_thread.c +++ b/core/shared/platform/alios/alios_thread.c @@ -249,6 +249,13 @@ os_mutex_lock(korp_mutex *mutex) return aos_mutex_lock(mutex, AOS_WAIT_FOREVER); } +int +os_mutex_trylock(korp_mutex *mutex) +{ + /* unsupported */ + return BHT_ERROR; +} + int os_mutex_unlock(korp_mutex *mutex) { diff --git a/core/shared/platform/common/freertos/freertos_thread.c b/core/shared/platform/common/freertos/freertos_thread.c index 9f68bc8f9..53bb02dc7 100644 --- a/core/shared/platform/common/freertos/freertos_thread.c +++ b/core/shared/platform/common/freertos/freertos_thread.c @@ -344,6 +344,13 @@ os_mutex_lock(korp_mutex *mutex) return ret == pdPASS ? BHT_OK : BHT_ERROR; } +int +os_mutex_trylock(korp_mutex *mutex) +{ + /* unsupported */ + return BHT_ERROR; +} + int os_mutex_unlock(korp_mutex *mutex) { diff --git a/core/shared/platform/common/posix/posix_thread.c b/core/shared/platform/common/posix/posix_thread.c index 498f5f5a2..6c16ba29b 100644 --- a/core/shared/platform/common/posix/posix_thread.c +++ b/core/shared/platform/common/posix/posix_thread.c @@ -154,6 +154,17 @@ os_mutex_lock(korp_mutex *mutex) return ret == 0 ? BHT_OK : BHT_ERROR; } +int +os_mutex_trylock(korp_mutex *mutex) +{ + int ret; + + assert(mutex); + ret = pthread_mutex_trylock(mutex); + + return ret == 0 ? BHT_OK : BHT_ERROR; +} + int os_mutex_unlock(korp_mutex *mutex) { diff --git a/core/shared/platform/esp-idf/espidf_thread.c b/core/shared/platform/esp-idf/espidf_thread.c index 637cd4177..3240adca2 100644 --- a/core/shared/platform/esp-idf/espidf_thread.c +++ b/core/shared/platform/esp-idf/espidf_thread.c @@ -75,6 +75,12 @@ os_mutex_lock(korp_mutex *mutex) return pthread_mutex_lock(mutex); } +int +os_mutex_trylock(korp_mutex *mutex) +{ + return pthread_mutex_trylock(mutex); +} + int os_mutex_unlock(korp_mutex *mutex) { @@ -230,4 +236,4 @@ int os_cond_broadcast(korp_cond *cond) { return pthread_cond_broadcast(cond); -} \ No newline at end of file +} diff --git a/core/shared/platform/include/platform_api_vmcore.h b/core/shared/platform/include/platform_api_vmcore.h index c803c12e4..c6df1dac8 100644 --- a/core/shared/platform/include/platform_api_vmcore.h +++ b/core/shared/platform/include/platform_api_vmcore.h @@ -103,6 +103,9 @@ os_mutex_destroy(korp_mutex *mutex); int os_mutex_lock(korp_mutex *mutex); +int +os_mutex_trylock(korp_mutex *mutex); + int os_mutex_unlock(korp_mutex *mutex); diff --git a/core/shared/platform/linux-sgx/sgx_thread.c b/core/shared/platform/linux-sgx/sgx_thread.c index d9412927b..e20c341c7 100644 --- a/core/shared/platform/linux-sgx/sgx_thread.c +++ b/core/shared/platform/linux-sgx/sgx_thread.c @@ -100,6 +100,16 @@ os_mutex_lock(korp_mutex *mutex) #endif } +int +os_mutex_trylock(korp_mutex *mutex) +{ +#ifndef SGX_DISABLE_PTHREAD + return pthread_mutex_lock(mutex); +#else + return 0; +#endif +} + int os_mutex_unlock(korp_mutex *mutex) { diff --git a/core/shared/platform/riot/riot_thread.c b/core/shared/platform/riot/riot_thread.c index a9062bfec..ba3ac025d 100644 --- a/core/shared/platform/riot/riot_thread.c +++ b/core/shared/platform/riot/riot_thread.c @@ -323,6 +323,13 @@ os_mutex_lock(korp_mutex *mutex) return 0; // Riot mutexes do not return until success } +int +os_mutex_trylock(korp_mutex *mutex) +{ + /* unsupported */ + return BHT_ERROR; +} + int os_mutex_unlock(korp_mutex *mutex) { diff --git a/core/shared/platform/rt-thread/rtt_platform.c b/core/shared/platform/rt-thread/rtt_platform.c index 6dc719f01..5a5a78e94 100644 --- a/core/shared/platform/rt-thread/rtt_platform.c +++ b/core/shared/platform/rt-thread/rtt_platform.c @@ -162,6 +162,13 @@ os_mutex_lock(korp_mutex *mutex) return rt_mutex_take(mutex, RT_WAITING_FOREVER); } +int +os_mutex_trylock(korp_mutex *mutex) +{ + /* unsupported */ + return BHT_ERROR; +} + int os_mutex_unlock(korp_mutex *mutex) { diff --git a/core/shared/platform/windows/win_thread.c b/core/shared/platform/windows/win_thread.c index abc36f2fc..f750b01da 100644 --- a/core/shared/platform/windows/win_thread.c +++ b/core/shared/platform/windows/win_thread.c @@ -534,6 +534,13 @@ os_mutex_lock(korp_mutex *mutex) return ret != WAIT_FAILED ? BHT_OK : BHT_ERROR; } +int +os_mutex_trylock(korp_mutex *mutex) +{ + /* unsupported */ + return BHT_ERROR; +} + int os_mutex_unlock(korp_mutex *mutex) { diff --git a/core/shared/platform/zephyr/zephyr_thread.c b/core/shared/platform/zephyr/zephyr_thread.c index 105d53993..5ea0490a5 100644 --- a/core/shared/platform/zephyr/zephyr_thread.c +++ b/core/shared/platform/zephyr/zephyr_thread.c @@ -451,6 +451,13 @@ os_mutex_lock(korp_mutex *mutex) return k_mutex_lock(mutex, K_FOREVER); } +int +os_mutex_trylock(korp_mutex *mutex) +{ + /* unsupported */ + return BHT_ERROR; +} + int os_mutex_unlock(korp_mutex *mutex) { diff --git a/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh b/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh index b8e3ddfec..61db39db0 100755 --- a/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh +++ b/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh @@ -37,7 +37,7 @@ run_aot_tests () { test_aot="${test_wasm%.wasm}.aot" test_json="${test_wasm%.wasm}.json" - + if [ -f ${test_wasm} ]; then expected=$(jq .exit_code ${test_json}) fi @@ -87,9 +87,9 @@ if [[ $MODE != "aot" ]];then if [ "${ret}" -eq 0 ]; then ret=${PIPESTATUS[0]} fi - + exit_code=${ret} - + deactivate else target_option=""