Stop abusing shared memory lock to protect exception (#2509)

Use a separate global lock instead.

Fixes: https://github.com/bytecodealliance/wasm-micro-runtime/issues/2407
This commit is contained in:
YAMAMOTO Takashi 2023-08-31 21:39:08 +09:00 committed by GitHub
parent 53d7027de0
commit 382d52fc05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 76 deletions

View File

@ -2377,12 +2377,7 @@ wasm_runtime_get_exec_env_singleton(WASMModuleInstanceCommon *module_inst_comm)
void void
wasm_set_exception(WASMModuleInstance *module_inst, const char *exception) wasm_set_exception(WASMModuleInstance *module_inst, const char *exception)
{ {
WASMExecEnv *exec_env = NULL; exception_lock(module_inst);
#if WASM_ENABLE_SHARED_MEMORY != 0
if (module_inst->memory_count > 0)
shared_memory_lock(module_inst->memories[0]);
#endif
if (exception) { if (exception) {
snprintf(module_inst->cur_exception, sizeof(module_inst->cur_exception), snprintf(module_inst->cur_exception, sizeof(module_inst->cur_exception),
"Exception: %s", exception); "Exception: %s", exception);
@ -2390,19 +2385,14 @@ wasm_set_exception(WASMModuleInstance *module_inst, const char *exception)
else { else {
module_inst->cur_exception[0] = '\0'; module_inst->cur_exception[0] = '\0';
} }
#if WASM_ENABLE_SHARED_MEMORY != 0 exception_unlock(module_inst);
if (module_inst->memory_count > 0)
shared_memory_unlock(module_inst->memories[0]);
#endif
#if WASM_ENABLE_THREAD_MGR != 0 #if WASM_ENABLE_THREAD_MGR != 0
exec_env = WASMExecEnv *exec_env =
wasm_clusters_search_exec_env((WASMModuleInstanceCommon *)module_inst); wasm_clusters_search_exec_env((WASMModuleInstanceCommon *)module_inst);
if (exec_env) { if (exec_env) {
wasm_cluster_spread_exception(exec_env, exception ? false : true); wasm_cluster_spread_exception(exec_env, exception);
} }
#else
(void)exec_env;
#endif #endif
} }
@ -2453,10 +2443,7 @@ wasm_copy_exception(WASMModuleInstance *module_inst, char *exception_buf)
{ {
bool has_exception = false; bool has_exception = false;
#if WASM_ENABLE_SHARED_MEMORY != 0 exception_lock(module_inst);
if (module_inst->memory_count > 0)
shared_memory_lock(module_inst->memories[0]);
#endif
if (module_inst->cur_exception[0] != '\0') { if (module_inst->cur_exception[0] != '\0') {
/* NULL is passed if the caller is not interested in getting the /* NULL is passed if the caller is not interested in getting the
* exception content, but only in knowing if an exception has been * exception content, but only in knowing if an exception has been
@ -2468,10 +2455,7 @@ wasm_copy_exception(WASMModuleInstance *module_inst, char *exception_buf)
sizeof(module_inst->cur_exception)); sizeof(module_inst->cur_exception));
has_exception = true; has_exception = true;
} }
#if WASM_ENABLE_SHARED_MEMORY != 0 exception_unlock(module_inst);
if (module_inst->memory_count > 0)
shared_memory_unlock(module_inst->memories[0]);
#endif
return has_exception; return has_exception;
} }

View File

@ -668,6 +668,16 @@ void
wasm_propagate_wasi_args(WASMModule *module); wasm_propagate_wasi_args(WASMModule *module);
#endif #endif
#if WASM_ENABLE_THREAD_MGR != 0
void
exception_lock(WASMModuleInstance *module_inst);
void
exception_unlock(WASMModuleInstance *module_inst);
#else
#define exception_lock(module_inst) (void)(module_inst)
#define exception_unlock(module_inst) (void)(module_inst)
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -16,10 +16,6 @@
#include "debug_engine.h" #include "debug_engine.h"
#endif #endif
#if WASM_ENABLE_SHARED_MEMORY != 0
#include "wasm_shared_memory.h"
#endif
typedef struct { typedef struct {
bh_list_link l; bh_list_link l;
void (*destroy_cb)(WASMCluster *); void (*destroy_cb)(WASMCluster *);
@ -32,6 +28,8 @@ static bh_list cluster_list_head;
static bh_list *const cluster_list = &cluster_list_head; static bh_list *const cluster_list = &cluster_list_head;
static korp_mutex cluster_list_lock; static korp_mutex cluster_list_lock;
static korp_mutex _exception_lock;
typedef void (*list_visitor)(void *, void *); typedef void (*list_visitor)(void *, void *);
static uint32 cluster_max_thread_num = CLUSTER_MAX_THREAD_NUM; static uint32 cluster_max_thread_num = CLUSTER_MAX_THREAD_NUM;
@ -52,6 +50,10 @@ thread_manager_init()
return false; return false;
if (os_mutex_init(&cluster_list_lock) != 0) if (os_mutex_init(&cluster_list_lock) != 0)
return false; return false;
if (os_mutex_init(&_exception_lock) != 0) {
os_mutex_destroy(&cluster_list_lock);
return false;
}
return true; return true;
} }
@ -66,6 +68,7 @@ thread_manager_destroy()
cluster = next; cluster = next;
} }
wasm_cluster_cancel_all_callbacks(); wasm_cluster_cancel_all_callbacks();
os_mutex_destroy(&_exception_lock);
os_mutex_destroy(&cluster_list_lock); os_mutex_destroy(&cluster_list_lock);
} }
@ -1240,72 +1243,56 @@ wasm_cluster_resume_all(WASMCluster *cluster)
os_mutex_unlock(&cluster->lock); os_mutex_unlock(&cluster->lock);
} }
struct spread_exception_data {
WASMExecEnv *skip;
const char *exception;
};
static void static void
set_exception_visitor(void *node, void *user_data) set_exception_visitor(void *node, void *user_data)
{ {
WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; const struct spread_exception_data *data = user_data;
WASMExecEnv *exec_env = (WASMExecEnv *)user_data; WASMExecEnv *exec_env = (WASMExecEnv *)node;
WASMModuleInstanceCommon *module_inst = get_module_inst(exec_env);
WASMModuleInstance *wasm_inst = (WASMModuleInstance *)module_inst;
if (curr_exec_env != exec_env) { if (exec_env != data->skip) {
WASMModuleInstance *curr_wasm_inst = WASMModuleInstance *wasm_inst =
(WASMModuleInstance *)get_module_inst(curr_exec_env); (WASMModuleInstance *)get_module_inst(exec_env);
exception_lock(wasm_inst);
if (data->exception != NULL) {
/* Only spread non "wasi proc exit" exception */ /* Only spread non "wasi proc exit" exception */
#if WASM_ENABLE_SHARED_MEMORY != 0 if (strcmp(data->exception, "wasi proc exit")) {
if (curr_wasm_inst->memory_count > 0) snprintf(wasm_inst->cur_exception,
shared_memory_lock(curr_wasm_inst->memories[0]); sizeof(wasm_inst->cur_exception), "Exception: %s",
#endif data->exception);
if (!strstr(wasm_inst->cur_exception, "wasi proc exit")) {
bh_memcpy_s(curr_wasm_inst->cur_exception,
sizeof(curr_wasm_inst->cur_exception),
wasm_inst->cur_exception,
sizeof(wasm_inst->cur_exception));
} }
#if WASM_ENABLE_SHARED_MEMORY != 0 }
if (curr_wasm_inst->memory_count > 0) else {
shared_memory_unlock(curr_wasm_inst->memories[0]); wasm_inst->cur_exception[0] = '\0';
#endif }
exception_unlock(wasm_inst);
/* Terminate the thread so it can exit from dead loops */ /* Terminate the thread so it can exit from dead loops */
set_thread_cancel_flags(curr_exec_env); if (data->exception != NULL) {
set_thread_cancel_flags(exec_env);
} }
}
static void
clear_exception_visitor(void *node, void *user_data)
{
WASMExecEnv *exec_env = (WASMExecEnv *)user_data;
WASMExecEnv *curr_exec_env = (WASMExecEnv *)node;
if (curr_exec_env != exec_env) {
WASMModuleInstance *curr_wasm_inst =
(WASMModuleInstance *)get_module_inst(curr_exec_env);
#if WASM_ENABLE_SHARED_MEMORY != 0
if (curr_wasm_inst->memory_count > 0)
shared_memory_lock(curr_wasm_inst->memories[0]);
#endif
curr_wasm_inst->cur_exception[0] = '\0';
#if WASM_ENABLE_SHARED_MEMORY != 0
if (curr_wasm_inst->memory_count > 0)
shared_memory_unlock(curr_wasm_inst->memories[0]);
#endif
} }
} }
void void
wasm_cluster_spread_exception(WASMExecEnv *exec_env, bool clear) wasm_cluster_spread_exception(WASMExecEnv *exec_env, const char *exception)
{ {
const bool has_exception = exception != NULL;
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
bh_assert(cluster); bh_assert(cluster);
struct spread_exception_data data;
data.skip = exec_env;
data.exception = exception;
os_mutex_lock(&cluster->lock); os_mutex_lock(&cluster->lock);
cluster->has_exception = !clear; cluster->has_exception = has_exception;
traverse_list(&cluster->exec_env_list, traverse_list(&cluster->exec_env_list, set_exception_visitor, &data);
clear ? clear_exception_visitor : set_exception_visitor,
exec_env);
os_mutex_unlock(&cluster->lock); os_mutex_unlock(&cluster->lock);
} }
@ -1353,3 +1340,21 @@ wasm_cluster_is_thread_terminated(WASMExecEnv *exec_env)
return is_thread_terminated; return is_thread_terminated;
} }
void
exception_lock(WASMModuleInstance *module_inst)
{
/*
* Note: this lock could be per module instance if desirable.
* We can revisit on AOT version bump.
* It probably doesn't matter though because the exception handling
* logic should not be executed too frequently anyway.
*/
os_mutex_lock(&_exception_lock);
}
void
exception_unlock(WASMModuleInstance *module_inst)
{
os_mutex_unlock(&_exception_lock);
}

View File

@ -139,7 +139,7 @@ WASMExecEnv *
wasm_clusters_search_exec_env(WASMModuleInstanceCommon *module_inst); wasm_clusters_search_exec_env(WASMModuleInstanceCommon *module_inst);
void void
wasm_cluster_spread_exception(WASMExecEnv *exec_env, bool clear); wasm_cluster_spread_exception(WASMExecEnv *exec_env, const char *exception);
WASMExecEnv * WASMExecEnv *
wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env); wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env);