diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 06d75a4897..fd4b26252b 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -4183,79 +4183,86 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, wasm_exec_env_set_cur_frame(exec_env, frame); - if (function->is_import_func) { + sigjmp_buf *context = os_create_stack_context(); + if (!os_is_returning_from_signal(exec_env->handle, context)) { + if (function->is_import_func) { #if WASM_ENABLE_MULTI_MODULE != 0 - if (function->import_module_inst) { - wasm_interp_call_func_import(module_inst, exec_env, function, - frame); - } - else + if (function->import_module_inst) { + wasm_interp_call_func_import(module_inst, exec_env, function, + frame); + } + else #endif - { - /* it is a native function */ - wasm_interp_call_func_native(module_inst, exec_env, function, - frame); + { + /* it is a native function */ + wasm_interp_call_func_native(module_inst, exec_env, function, + frame); + } } - } - else { + else { #if WASM_ENABLE_LAZY_JIT != 0 - /* Fast JIT to LLVM JIT tier-up is enabled */ + /* Fast JIT to LLVM JIT tier-up is enabled */ #if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 - /* Fast JIT and LLVM JIT are both enabled, call llvm jit function - if it is compiled, else call fast jit function */ - uint32 func_idx = (uint32)(function - module_inst->e->functions); - if (module_inst->module->func_ptrs_compiled - [func_idx - module_inst->module->import_function_count]) { + /* Fast JIT and LLVM JIT are both enabled, call llvm jit function + if it is compiled, else call fast jit function */ + uint32 func_idx = (uint32)(function - module_inst->e->functions); + if (module_inst->module->func_ptrs_compiled + [func_idx - module_inst->module->import_function_count]) { + llvm_jit_call_func_bytecode(module_inst, exec_env, function, + argc, argv); + /* For llvm jit, the results have been stored in argv, + no need to copy them from stack frame again */ + copy_argv_from_frame = false; + } + else { + fast_jit_call_func_bytecode(module_inst, exec_env, function, + frame); + } +#elif WASM_ENABLE_JIT != 0 + /* Only LLVM JIT is enabled */ llvm_jit_call_func_bytecode(module_inst, exec_env, function, argc, argv); /* For llvm jit, the results have been stored in argv, no need to copy them from stack frame again */ copy_argv_from_frame = false; - } - else { - fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); - } -#elif WASM_ENABLE_JIT != 0 - /* Only LLVM JIT is enabled */ - llvm_jit_call_func_bytecode(module_inst, exec_env, function, argc, - argv); - /* For llvm jit, the results have been stored in argv, - no need to copy them from stack frame again */ - copy_argv_from_frame = false; #elif WASM_ENABLE_FAST_JIT != 0 - /* Only Fast JIT is enabled */ - fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); + /* Only Fast JIT is enabled */ + fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); #else - /* Both Fast JIT and LLVM JIT are disabled */ - wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame); + /* Both Fast JIT and LLVM JIT are disabled */ + wasm_interp_call_func_bytecode(module_inst, exec_env, function, + frame); #endif #else /* else of WASM_ENABLE_LAZY_JIT != 0 */ - /* Fast JIT to LLVM JIT tier-up is enabled */ + /* Fast JIT to LLVM JIT tier-up is enabled */ #if WASM_ENABLE_JIT != 0 - /* LLVM JIT is enabled */ - llvm_jit_call_func_bytecode(module_inst, exec_env, function, argc, - argv); - /* For llvm jit, the results have been stored in argv, - no need to copy them from stack frame again */ - copy_argv_from_frame = false; + /* LLVM JIT is enabled */ + llvm_jit_call_func_bytecode(module_inst, exec_env, function, argc, + argv); + /* For llvm jit, the results have been stored in argv, + no need to copy them from stack frame again */ + copy_argv_from_frame = false; #elif WASM_ENABLE_FAST_JIT != 0 - /* Fast JIT is enabled */ - fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); + /* Fast JIT is enabled */ + fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); #else - /* Both Fast JIT and LLVM JIT are disabled */ - wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame); + /* Both Fast JIT and LLVM JIT are disabled */ + wasm_interp_call_func_bytecode(module_inst, exec_env, function, + frame); #endif #endif /* end of WASM_ENABLE_LAZY_JIT != 0 */ - (void)wasm_interp_call_func_bytecode; + (void)wasm_interp_call_func_bytecode; #if WASM_ENABLE_FAST_JIT != 0 - (void)fast_jit_call_func_bytecode; + (void)fast_jit_call_func_bytecode; #endif + } } + os_remove_stack_context(exec_env->handle, context); /* Output the return value to the caller */ if (!wasm_get_exception(module_inst)) { diff --git a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c index d4d9023e34..44dfbded35 100644 --- a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c +++ b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c @@ -150,18 +150,6 @@ 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) { diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c index 492ed66b10..ee219d9e5a 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.c +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -1059,6 +1059,10 @@ set_exception_visitor(void *node, void *user_data) bh_memcpy_s(curr_wasm_inst->cur_exception, sizeof(curr_wasm_inst->cur_exception), wasm_inst->cur_exception, sizeof(wasm_inst->cur_exception)); + + if (curr_exec_env->handle) + os_thread_signal(curr_exec_env->handle, SIGUSR1); + /* Terminate the thread so it can exit from dead loops */ set_thread_cancel_flags(curr_exec_env); } diff --git a/core/shared/platform/common/posix/posix_thread.c b/core/shared/platform/common/posix/posix_thread.c index 58370c203f..8aad1d4f37 100644 --- a/core/shared/platform/common/posix/posix_thread.c +++ b/core/shared/platform/common/posix/posix_thread.c @@ -9,6 +9,13 @@ #include "platform_api_vmcore.h" #include "platform_api_extension.h" +#include "bh_hashmap.h" +#include + +#define STACK_CTX_SIG SIGUSR1 +static os_thread_local_attribute sigset_t newmask, oldmask; +bh_stack_context_handler_t stack_context_handler; + typedef struct { thread_start_routine_t start; void *arg; @@ -47,6 +54,164 @@ os_thread_wrapper(void *arg) return NULL; } +static void +stack_context_sig_handler(int sig) +{ + assert(sig == STACK_CTX_SIG); + korp_tid self = pthread_self(); + + /* Get latest stack context (first in the list) for current thread */ + bh_list *context_list = bh_hash_map_find(stack_context_handler.contexts, + (void *)(uintptr_t)self); + assert(context_list); + sigjmp_buf *context = bh_list_first_elem(context_list); + assert(context); + + siglongjmp(*context, 1); +} + +sigjmp_buf * +os_create_stack_context(korp_tid handle) +{ + return BH_MALLOC(sizeof(sigjmp_buf)); +} + +static void +free_stack_context_list(void *stack_context_list) +{ + bh_list *context_list = (bh_list *)stack_context_list; + + sigjmp_buf *context = bh_list_first_elem(context_list); + while (context) { + sigjmp_buf *next = bh_list_elem_next(context); + BH_FREE(context); + context = next; + }; + + BH_FREE(context_list); +} + +static inline void +stack_ctx_block_sig_and_lock() +{ + sigemptyset(&newmask); + sigaddset(&newmask, STACK_CTX_SIG); + pthread_sigmask(SIG_BLOCK, &newmask, &oldmask); + + os_mutex_lock(&stack_context_handler.lock); +} + +static inline void +stack_ctx_unlock_and_unblock_sig() +{ + os_mutex_unlock(&stack_context_handler.lock); + pthread_sigmask(SIG_SETMASK, &oldmask, NULL); +} + +bool +os_stack_contexts_init() +{ + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = stack_context_sig_handler; + sigfillset(&act.sa_mask); + if (sigaction(STACK_CTX_SIG, &act, NULL) < 0) { + LOG_ERROR("failed to set signal handler"); + return false; + } + + if (os_mutex_init(&stack_context_handler.lock) != 0) { + LOG_ERROR("failed to init context mutex"); + return false; + } + + stack_context_handler.contexts = bh_hash_map_create( + 32, false, (HashFunc)thread_handle_hash, + (KeyEqualFunc)thread_handle_equal, NULL, free_stack_context_list); + if (stack_context_handler.contexts == NULL) { + LOG_ERROR("failed to init stack contexts"); + return false; + } + + return true; +} + +void +os_stack_contexts_destroy() +{ + if (!bh_hash_map_destroy(stack_context_handler.contexts)) + LOG_ERROR("failed to destroy stack context map"); + stack_context_handler.contexts = NULL; + + if (os_mutex_destroy(&stack_context_handler.lock) != BHT_OK) + LOG_ERROR("failed to destroy stack contexts"); +} + +void +os_remove_stack_context(korp_tid handle, sigjmp_buf *context) +{ + korp_tid self = pthread_self(); + + stack_ctx_block_sig_and_lock(); + + bh_list *context_list = bh_hash_map_find(stack_context_handler.contexts, + (void *)(uintptr_t)self); + assert(context_list); + int ret = bh_list_remove(context_list, context); + assert(ret == BH_LIST_SUCCESS); + BH_FREE(context); + + stack_ctx_unlock_and_unblock_sig(); +} + +bool +os_is_returning_from_signal(korp_tid handle, sigjmp_buf *context) +{ + if (sigsetjmp(*context, 1)) + return true; + + stack_ctx_block_sig_and_lock(); + + /* Get or create list of stack contexts for current thread */ + bh_list *context_list = bh_hash_map_find(stack_context_handler.contexts, + (void *)(uintptr_t)handle); + if (!context_list) { + context_list = BH_MALLOC(sizeof(bh_list)); + if (!context_list) { + LOG_ERROR("failed to allocate stack context list"); + goto unlock; + } + + int ret_list = bh_list_init(context_list); + if (ret_list != BH_LIST_SUCCESS) { + LOG_ERROR("failed to initialize stack context list"); + goto free_list_and_unlock; + } + + bool ret_map = + bh_hash_map_insert(stack_context_handler.contexts, + (void *)(uintptr_t)handle, context_list); + if (!ret_map) { + LOG_ERROR("failed to insert stack context list into map"); + goto free_list_and_unlock; + } + } + + /* Add stack context to stack context list */ + int bh_res = bh_list_insert(context_list, context); + if (bh_res != BH_LIST_SUCCESS) { + LOG_ERROR("failed to insert stack context into list"); + goto free_list_and_unlock; + } + goto unlock; + +free_list_and_unlock: + BH_FREE(context_list); +unlock: + stack_ctx_unlock_and_unblock_sig(); + return false; +} + int os_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, void *arg, unsigned int stack_size, int prio) @@ -89,6 +254,12 @@ os_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, return BHT_OK; } +int +os_thread_signal(korp_tid tid, int sig) +{ + return pthread_kill(tid, sig); +} + int os_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, unsigned int stack_size) diff --git a/core/shared/platform/include/platform_api_extension.h b/core/shared/platform/include/platform_api_extension.h index 42baad74f7..ce75698aea 100644 --- a/core/shared/platform/include/platform_api_extension.h +++ b/core/shared/platform/include/platform_api_extension.h @@ -17,6 +17,20 @@ extern "C" { #endif +#include +/** + * @brief Structure used to track stack contexts. It contains a map to save the stack contexts for each thread. + * A list is used to handles stack contexts created in nested functions (for the + * same thread). Stack contexts are used by the signal handler to restore a + * previously saved state. + */ +typedef struct { + void *contexts; + korp_mutex lock; +} bh_stack_context_handler_t; +extern bh_stack_context_handler_t stack_context_handler; + /*************************************************** * * * Extension interface * @@ -49,6 +63,9 @@ int os_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, unsigned int stack_size); +int +os_thread_signal(korp_tid p_tid, int sig); + /** * Creates a thread with priority * @@ -280,6 +297,55 @@ os_sem_getvalue(korp_sem *sem, int *sval); int os_sem_unlink(const char *name); +/****************************************** + * Functions to handle stack contexts + ******************************************/ + +/** + * @brief Initializes the data structures to handle stack contexts + * + * @return true if success, false otherwise + */ +bool +os_stack_contexts_init(); + +/** + * @brief Destroys the data structures used for stack contexts + * + */ +void +os_stack_contexts_destroy(); + +/** + * @brief Creates and returns an empty stack context for the current thread + * + * @return sigjmp_buf* pointer to stack context created + */ +sigjmp_buf * +os_create_stack_context(); + +/** + * @brief Removes the stack context received from the stack contexts tracked for + * the specified thread. Called before the stack context is invalidated + * by the function (in which the stack context was created) returning. + * + * @param handle thread to remove the context from + * @param stack_context pointer to the stack pointer to remove + */ +void +os_remove_stack_context(korp_tid handle, sigjmp_buf *stack_context); + +/** + * @brief Returns true if returning from a signal handler; otherwise, adds the + * stack context received to the tracked stack contexts for the specified thread + * + * @param handle thread in which the context was created + * @param stack_context pointer to the stack pointer to use + * @return true if returning from a signal handler, false otherwise + */ +bool +os_is_returning_from_signal(korp_tid handle, sigjmp_buf *stack_context); + /**************************************************** * Section 2 * * Socket support * diff --git a/core/shared/utils/bh_common.h b/core/shared/utils/bh_common.h index edb962eb1c..52d65ca231 100644 --- a/core/shared/utils/bh_common.h +++ b/core/shared/utils/bh_common.h @@ -66,6 +66,18 @@ bh_strdup(const char *s); char * wa_strdup(const char *s); +static inline uint32 +thread_handle_hash(void *handle) +{ + return (uint32)(uintptr_t)handle; +} + +static inline bool +thread_handle_equal(void *h1, void *h2) +{ + return (uint32)(uintptr_t)h1 == (uint32)(uintptr_t)h2 ? true : false; +} + #ifdef __cplusplus } #endif diff --git a/product-mini/platforms/posix/main.c b/product-mini/platforms/posix/main.c index e04745c68d..64ec64b373 100644 --- a/product-mini/platforms/posix/main.c +++ b/product-mini/platforms/posix/main.c @@ -621,6 +621,9 @@ main(int argc, char *argv[]) ns_lookup_pool_size); #endif + if (!os_stack_contexts_init()) + goto fail3; + /* instantiate the module */ if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, stack_size, heap_size, @@ -693,6 +696,8 @@ main(int argc, char *argv[]) unregister_and_unload_native_libs(native_handle_count, native_handle_list); #endif + os_stack_contexts_destroy(); + /* destroy runtime environment */ wasm_runtime_destroy(); diff --git a/samples/wasi-threads/wasm-apps/thread_termination.c b/samples/wasi-threads/wasm-apps/thread_termination.c index dfc228eb3c..77d7e38a60 100644 --- a/samples/wasi-threads/wasm-apps/thread_termination.c +++ b/samples/wasi-threads/wasm-apps/thread_termination.c @@ -17,6 +17,7 @@ #define TEST_TERMINATION_BY_TRAP 0 // Otherwise test `proc_exit` termination #define TEST_TERMINATION_IN_MAIN_THREAD 1 +#define TEST_ATOMIC_WAIT 1 // Otherwise test `sleep` #define TIMEOUT_SECONDS 10 #define NUM_THREADS 3 @@ -30,9 +31,11 @@ typedef struct { void run_long_task() { - // Busy waiting to be interruptible by trap or `proc_exit` - for (int i = 0; i < TIMEOUT_SECONDS; i++) - sleep(1); +#if TEST_ATOMIC_WAIT == 1 + __builtin_wasm_memory_atomic_wait32(0, 0, -1); +#else + sleep(TIMEOUT_SECONDS); +#endif } void