Просмотр исходного кода

Add an API to terminate instance (#2538)

Add API wasm_runtime_terminate to terminate a module instance
by setting "terminated by user" exception to the module instance.

And update the product-mini of posix platforms.

Note: this doesn't work for some situations like blocking system calls.
YAMAMOTO Takashi 2 лет назад
Родитель
Сommit
a6fda9b7ab

+ 24 - 3
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)

+ 4 - 0
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,

+ 21 - 0
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:

+ 14 - 2
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);

+ 1 - 1
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);

+ 72 - 0
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