From e35579914fe1d3222cb80cc4eeda736c1960d75b Mon Sep 17 00:00:00 2001 From: eloparco Date: Fri, 6 Jan 2023 16:52:37 +0000 Subject: [PATCH] feat(wasi-threads): handle thread exit --- core/iwasm/aot/aot_runtime.c | 4 +- core/iwasm/common/wasm_application.c | 4 ++ core/iwasm/common/wasm_runtime_common.c | 4 ++ core/iwasm/interpreter/wasm_runtime.c | 4 +- .../lib_wasi_threads_wrapper.c | 6 +-- samples/wasi-threads/wasm-apps/CMakeLists.txt | 2 +- ...ion_propagation.c => thread_termination.c} | 39 +++++++++++++++---- 7 files changed, 48 insertions(+), 15 deletions(-) rename samples/wasi-threads/wasm-apps/{exception_propagation.c => thread_termination.c} (68%) diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index 133c5e38ad..d357b5a852 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -902,7 +902,9 @@ create_exports(AOTModuleInstance *module_inst, AOTModule *module, static bool clear_wasi_proc_exit_exception(AOTModuleInstance *module_inst) { -#if WASM_ENABLE_LIBC_WASI != 0 + /* With WASI threads, proc_exit exception is not cleared because it has to + * be detected and propagated to the other threads in the process */ +#if WASM_ENABLE_LIBC_WASI != 0 && WASM_ENABLE_LIB_WASI_THREADS == 0 const char *exception = aot_get_exception(module_inst); if (exception && !strcmp(exception, "Exception: wasi proc exit")) { /* The "wasi proc exit" exception is thrown by native lib to diff --git a/core/iwasm/common/wasm_application.c b/core/iwasm/common/wasm_application.c index 8445652f64..351150b70f 100644 --- a/core/iwasm/common/wasm_application.c +++ b/core/iwasm/common/wasm_application.c @@ -230,6 +230,10 @@ wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, int32 argc, wasm_runtime_dump_perf_profiling(module_inst); #endif + const char *exception = wasm_runtime_get_exception(module_inst); + if (exception && strcmp(exception, "Exception: wasi proc exit") == 0) + wasm_runtime_clear_exception(module_inst); + return (ret && !wasm_runtime_get_exception(module_inst)) ? true : false; } diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 88dd1c19d4..2fe242e572 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -1786,6 +1786,10 @@ wasm_runtime_call_wasm(WASMExecEnv *exec_env, if (new_argv != argv) { wasm_runtime_free(new_argv); } +#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_LIB_WASI_THREADS != 0 + if (wasm_runtime_get_exception(exec_env->module_inst)) + wasm_cluster_spread_exception(exec_env); +#endif return false; } diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index d5cd1bf9ef..ff3105d57b 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -2033,7 +2033,9 @@ wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name) static bool clear_wasi_proc_exit_exception(WASMModuleInstance *module_inst) { -#if WASM_ENABLE_LIBC_WASI != 0 + /* With WASI threads, proc_exit exception is not cleared because it has to + * be detected and propagated to the other threads in the process */ +#if WASM_ENABLE_LIBC_WASI != 0 && WASM_ENABLE_LIB_WASI_THREADS == 0 const char *exception = wasm_get_exception(module_inst); if (exception && !strcmp(exception, "Exception: wasi proc exit")) { /* The "wasi proc exit" exception is thrown by native lib to diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c index 044e5d22e1..4d1db94664 100644 --- a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -50,7 +50,6 @@ static void * thread_start(void *arg) { wasm_exec_env_t exec_env = (wasm_exec_env_t)arg; - wasm_module_inst_t module_inst = get_module_inst(exec_env); ThreadStartArg *thread_arg = exec_env->thread_arg; uint32 argv[2]; @@ -58,10 +57,7 @@ thread_start(void *arg) argv[0] = thread_arg->thread_id; argv[1] = thread_arg->arg; - if (!wasm_runtime_call_wasm(exec_env, thread_arg->start_func, 2, argv)) { - if (wasm_runtime_get_exception(module_inst)) - wasm_cluster_spread_exception(exec_env); - } + wasm_runtime_call_wasm(exec_env, thread_arg->start_func, 2, argv); // Routine exit deallocate_thread_id(thread_arg->thread_id); diff --git a/samples/wasi-threads/wasm-apps/CMakeLists.txt b/samples/wasi-threads/wasm-apps/CMakeLists.txt index b2d7638712..0da9aa749f 100644 --- a/samples/wasi-threads/wasm-apps/CMakeLists.txt +++ b/samples/wasi-threads/wasm-apps/CMakeLists.txt @@ -36,4 +36,4 @@ function (compile_sample SOURCE_FILE) endfunction () compile_sample(no_pthread.c wasi_thread_start.S) -compile_sample(exception_propagation.c wasi_thread_start.S) +compile_sample(thread_termination.c wasi_thread_start.S) \ No newline at end of file diff --git a/samples/wasi-threads/wasm-apps/exception_propagation.c b/samples/wasi-threads/wasm-apps/thread_termination.c similarity index 68% rename from samples/wasi-threads/wasm-apps/exception_propagation.c rename to samples/wasi-threads/wasm-apps/thread_termination.c index 9e07675be3..2adaa621f9 100644 --- a/samples/wasi-threads/wasm-apps/exception_propagation.c +++ b/samples/wasi-threads/wasm-apps/thread_termination.c @@ -15,6 +15,9 @@ #include "wasi_thread_start.h" +#define TEST_TERMINATION_BY_TRAP 0 // Otherwise test `proc_exit` termination +#define TEST_TERMINATION_IN_MAIN_THREAD 1 + #define TIMEOUT_SECONDS 10 #define NUM_THREADS 3 static sem_t sem; @@ -27,7 +30,7 @@ typedef struct { void run_long_task() { - // Busy waiting to be interruptible by exception + // Busy waiting to be interruptible by trap or `proc_exit` for (int i = 0; i < TIMEOUT_SECONDS; i++) sleep(1); } @@ -39,18 +42,22 @@ __wasi_thread_start_C(int thread_id, int *start_arg) if (data->throw_exception) { // Wait for all other threads (including main thread) to be ready - printf("Waiting before throwing exception\n"); + printf("Waiting before terminating\n"); for (int i = 0; i < NUM_THREADS; i++) sem_wait(&sem); - printf("Throwing exception\n"); + printf("Force termination\n"); +#if TEST_TERMINATION_BY_TRAP == 1 __builtin_trap(); +#else + __wasi_proc_exit(1); +#endif } else { printf("Thread running\n"); sem_post(&sem); - run_long_task(); // Wait to be interrupted by exception + run_long_task(); // Wait to be interrupted assert(false && "Unreachable"); } } @@ -74,19 +81,26 @@ main(int argc, char **argv) } } - // Create a thread that throws an exception +// Create a thread that forces termination through trap or `proc_exit` +#if TEST_TERMINATION_IN_MAIN_THREAD == 1 + data[0].throw_exception = false; +#else data[0].throw_exception = true; +#endif thread_id = __wasi_thread_spawn(&data[0]); if (thread_id < 0) { printf("Failed to create thread: %d\n", thread_id); return EXIT_FAILURE; } + // Create two additional threads to test exception propagation + data[1].throw_exception = false; thread_id = __wasi_thread_spawn(&data[1]); if (thread_id < 0) { printf("Failed to create thread: %d\n", thread_id); return EXIT_FAILURE; } + data[2].throw_exception = false; thread_id = __wasi_thread_spawn(&data[2]); if (thread_id < 0) { printf("Failed to create thread: %d\n", thread_id); @@ -96,8 +110,19 @@ main(int argc, char **argv) printf("Main thread running\n"); sem_post(&sem); - run_long_task(); // Wait to be interrupted by exception - assert(false && "Unreachable"); +#if TEST_TERMINATION_IN_MAIN_THREAD == 1 + + printf("Force termination (main thread)\n"); +#if TEST_TERMINATION_BY_TRAP == 1 + __builtin_trap(); +#else /* TEST_TERMINATION_BY_TRAP */ + __wasi_proc_exit(1); +#endif /* TEST_TERMINATION_BY_TRAP */ + +#else /* TEST_TERMINATION_IN_MAIN_THREAD */ + run_long_task(); // Wait to be interrupted + assert(false && "Unreachable"); +#endif /* TEST_TERMINATION_IN_MAIN_THREAD */ return EXIT_SUCCESS; } \ No newline at end of file