diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 7cac9e6432..3a2f44c5f0 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -2342,8 +2342,8 @@ wasm_runtime_get_exec_env_singleton(WASMModuleInstanceCommon *module_inst_comm) return module_inst->exec_env_singleton; } -void -wasm_set_exception(WASMModuleInstance *module_inst, const char *exception) +static void +wasm_set_exception_local(WASMModuleInstance *module_inst, const char *exception) { exception_lock(module_inst); if (exception) { @@ -2354,13 +2354,22 @@ wasm_set_exception(WASMModuleInstance *module_inst, const char *exception) module_inst->cur_exception[0] = '\0'; } exception_unlock(module_inst); +} +void +wasm_set_exception(WASMModuleInstance *module_inst, const char *exception) +{ #if WASM_ENABLE_THREAD_MGR != 0 WASMExecEnv *exec_env = wasm_clusters_search_exec_env((WASMModuleInstanceCommon *)module_inst); if (exec_env) { - wasm_cluster_spread_exception(exec_env, exception); + wasm_cluster_set_exception(exec_env, exception); } + else { + wasm_set_exception_local(module_inst, exception); + } +#else + wasm_set_exception_local(module_inst, exception); #endif } @@ -2468,6 +2477,18 @@ wasm_runtime_clear_exception(WASMModuleInstanceCommon *module_inst_comm) wasm_runtime_set_exception(module_inst_comm, NULL); } +#if WASM_ENABLE_THREAD_MGR != 0 +void +wasm_runtime_terminate(WASMModuleInstanceCommon *module_inst_comm) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + wasm_set_exception(module_inst, "terminated by user"); +} +#endif + void wasm_runtime_set_custom_data_internal( WASMModuleInstanceCommon *module_inst_comm, void *custom_data) diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index b9b0d0bf6b..4e3bc80af7 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -675,6 +675,10 @@ wasm_runtime_get_exception(WASMModuleInstanceCommon *module); WASM_RUNTIME_API_EXTERN void wasm_runtime_clear_exception(WASMModuleInstanceCommon *module_inst); +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_terminate(WASMModuleInstanceCommon *module); + /* Internal API */ void wasm_runtime_set_custom_data_internal(WASMModuleInstanceCommon *module_inst, diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index 589d8af00a..559f3d22e5 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -894,6 +894,27 @@ wasm_runtime_set_exception(wasm_module_inst_t module_inst, WASM_RUNTIME_API_EXTERN void wasm_runtime_clear_exception(wasm_module_inst_t module_inst); +/** + * Terminate the WASM module instance. + * + * This function causes the module instance fail as if it raised a trap. + * + * This is intended to be used in situations like: + * + * - A thread is executing the WASM module instance + * (eg. it's in the middle of `wasm_application_execute_main`) + * + * - Another thread has a copy of `wasm_module_inst_t` of + * the module instance and wants to terminate it asynchronously. + * + * This function is provided only when WAMR is built with threading enabled. + * (`WASM_ENABLE_THREAD_MGR=1`) + * + * @param module_inst the WASM module instance + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_terminate(wasm_module_inst_t module_inst); + /** * Set custom data to WASM module instance. * Note: diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c index 95f0d4267a..06be3bc89d 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.c +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -1061,6 +1061,15 @@ set_thread_cancel_flags(WASMExecEnv *exec_env) os_mutex_unlock(&exec_env->wait_lock); } +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); +} + int32 wasm_cluster_cancel_thread(WASMExecEnv *exec_env) { @@ -1266,18 +1275,21 @@ set_exception_visitor(void *node, void *user_data) if (data->exception != NULL) { set_thread_cancel_flags(exec_env); } + else { + clear_thread_cancel_flags(exec_env); + } } } void -wasm_cluster_spread_exception(WASMExecEnv *exec_env, const char *exception) +wasm_cluster_set_exception(WASMExecEnv *exec_env, const char *exception) { const bool has_exception = exception != NULL; WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); bh_assert(cluster); struct spread_exception_data data; - data.skip = exec_env; + data.skip = NULL; data.exception = exception; os_mutex_lock(&cluster->lock); diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.h b/core/iwasm/libraries/thread-mgr/thread_manager.h index 0280119fc3..19186f5973 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.h +++ b/core/iwasm/libraries/thread-mgr/thread_manager.h @@ -139,7 +139,7 @@ WASMExecEnv * wasm_clusters_search_exec_env(WASMModuleInstanceCommon *module_inst); void -wasm_cluster_spread_exception(WASMExecEnv *exec_env, const char *exception); +wasm_cluster_set_exception(WASMExecEnv *exec_env, const char *exception); WASMExecEnv * wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env); diff --git a/product-mini/platforms/posix/main.c b/product-mini/platforms/posix/main.c index 6d4ada662a..d61557022b 100644 --- a/product-mini/platforms/posix/main.c +++ b/product-mini/platforms/posix/main.c @@ -97,6 +97,11 @@ print_help() #if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); #endif +#if WASM_ENABLE_THREAD_MGR != 0 + printf(" --timeout=ms Set the maximum execution time in ms.\n"); + printf(" If it expires, the runtime aborts the execution\n"); + printf(" with a trap.\n"); +#endif #if WASM_ENABLE_DEBUG_INTERP != 0 printf(" -g=ip:port Set the debug sever address, default is debug disabled\n"); printf(" if port is 0, then a random port will be used\n"); @@ -488,6 +493,37 @@ dump_pgo_prof_data(wasm_module_inst_t module_inst, const char *path) } #endif +#if WASM_ENABLE_THREAD_MGR != 0 +struct timeout_arg { + uint32 timeout_ms; + wasm_module_inst_t inst; + _Atomic bool cancel; +}; + +void * +timeout_thread(void *vp) +{ + const struct timeout_arg *arg = vp; + uint32 left = arg->timeout_ms; + while (!arg->cancel) { + uint32 ms; + if (left >= 100) { + ms = 100; + } + else { + ms = left; + } + os_usleep((uint64)ms * 1000); + left -= ms; + if (left == 0) { + wasm_runtime_terminate(arg->inst); + break; + } + } + return NULL; +} +#endif + int main(int argc, char *argv[]) { @@ -546,6 +582,9 @@ main(int argc, char *argv[]) #if WASM_ENABLE_STATIC_PGO != 0 const char *gen_prof_file = NULL; #endif +#if WASM_ENABLE_THREAD_MGR != 0 + int timeout_ms = -1; +#endif /* Process options. */ for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { @@ -742,6 +781,13 @@ main(int argc, char *argv[]) wasm_runtime_set_max_thread_num(atoi(argv[0] + 14)); } #endif +#if WASM_ENABLE_THREAD_MGR != 0 + else if (!strncmp(argv[0], "--timeout=", 10)) { + if (argv[0][10] == '\0') + return print_help(); + timeout_ms = atoi(argv[0] + 10); + } +#endif #if WASM_ENABLE_DEBUG_INTERP != 0 else if (!strncmp(argv[0], "-g=", 3)) { char *port_str = strchr(argv[0] + 3, ':'); @@ -902,6 +948,22 @@ main(int argc, char *argv[]) } #endif +#if WASM_ENABLE_THREAD_MGR != 0 + struct timeout_arg timeout_arg; + korp_tid timeout_tid; + if (timeout_ms >= 0) { + timeout_arg.timeout_ms = timeout_ms; + timeout_arg.inst = wasm_module_inst; + timeout_arg.cancel = false; + ret = os_thread_create(&timeout_tid, timeout_thread, &timeout_arg, + APP_THREAD_STACK_SIZE_DEFAULT); + if (ret != 0) { + printf("Failed to start timeout\n"); + goto fail5; + } + } +#endif + ret = 0; if (is_repl_mode) { app_instance_repl(wasm_module_inst); @@ -932,6 +994,16 @@ main(int argc, char *argv[]) dump_pgo_prof_data(wasm_module_inst, gen_prof_file); #endif +#if WASM_ENABLE_THREAD_MGR != 0 + if (timeout_ms >= 0) { + timeout_arg.cancel = true; + os_thread_join(timeout_tid, NULL); + } +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 +fail5: +#endif #if WASM_ENABLE_DEBUG_INTERP != 0 fail4: #endif