diff --git a/cli/main.c b/cli/main.c index 5475d20e..c25e7061 100644 --- a/cli/main.c +++ b/cli/main.c @@ -26,6 +26,7 @@ enum longopt { opt_repl, opt_repl_prompt, opt_print_stats, + opt_timeout, opt_trace, opt_version, opt_wasi, @@ -95,6 +96,12 @@ static const struct option longopts[] = { NULL, opt_print_stats, }, + { + "timeout", + required_argument, + NULL, + opt_timeout, + }, { "trace", required_argument, @@ -147,6 +154,7 @@ static const char *opt_metavars[] = { [opt_wasi_env] = "NAME=VAR", [opt_wasi_dir] = "DIR", [opt_wasi_mapdir] = "GUEST_DIR::HOST_DIR", + [opt_timeout] = "TIMEOUT_MS", [opt_trace] = "LEVEL", [opt_repl_prompt] = "STRING", [opt_max_frames] = "NUMBER_OF_FRAMES", @@ -208,6 +216,7 @@ main(int argc, char *const *argv) bool might_need_help = true; int exit_status = 1; + int timeout_ms = -1; #if defined(__NuttX__) xlog_tracing = 0; @@ -231,8 +240,8 @@ main(int argc, char *const *argv) opts->load_options.generate_resulttype_cellidx = false; break; case opt_invoke: - ret = toywasm_repl_invoke(state, NULL, optarg, NULL, - true); + ret = toywasm_repl_invoke(state, NULL, optarg, + timeout_ms, NULL, true); if (ret != 0) { goto fail; } @@ -265,6 +274,9 @@ main(int argc, char *const *argv) case opt_print_stats: opts->print_stats = true; break; + case opt_timeout: + timeout_ms = atoi(optarg); + break; case opt_trace: xlog_tracing = atoi(optarg); break; @@ -354,8 +366,8 @@ main(int argc, char *const *argv) } #endif uint32_t wasi_exit_code = 0; - ret = toywasm_repl_invoke(state, NULL, "_start", &wasi_exit_code, - false); + ret = toywasm_repl_invoke(state, NULL, "_start", timeout_ms, + &wasi_exit_code, false); if (ret != 0) { xlog_error("invoke failed with %d", ret); /* diff --git a/cli/repl.c b/cli/repl.c index 1b5b9f3e..29e3bf39 100644 --- a/cli/repl.c +++ b/cli/repl.c @@ -31,6 +31,7 @@ #include "repl.h" #include "report.h" #include "suspend.h" +#include "timeutil.h" #include "toywasm_version.h" #include "type.h" #include "usched.h" @@ -809,18 +810,46 @@ unescape(char *p0, size_t *lenp) static int exec_func(struct exec_context *ctx, uint32_t funcidx, const struct resulttype *ptype, const struct resulttype *rtype, - const struct val *param, struct val *result, + const struct val *param, struct val *result, int timeout_ms, const struct trap_info **trapp) { + struct timespec abstimeout; int ret; - ret = instance_execute_func(ctx, funcidx, ptype, rtype, param, result); - ret = instance_execute_handle_restart(ctx, ret); *trapp = NULL; + if (timeout_ms > 0) { + const static atomic_uint one = 1; + ret = abstime_from_reltime_ms(CLOCK_MONOTONIC, &abstimeout, + timeout_ms); + if (ret != 0) { + goto fail; + } + ctx->intrp = &one; + } + ret = instance_execute_func(ctx, funcidx, ptype, rtype, param, result); + do { + if (ret == ETOYWASMUSERINTERRUPT) { + struct timespec now; + int ret1; + assert(timeout_ms > 0); + ret1 = timespec_now(CLOCK_MONOTONIC, &now); + if (ret1 != 0) { + ret = ret1; + goto fail; + } + if (timespec_cmp(&now, &abstimeout) > 0) { + xlog_error("execution timed out"); + ret = ETIMEDOUT; + goto fail; + } + } + ret = instance_execute_handle_restart_once(ctx, ret); + } while (IS_RESTARTABLE(ret)); if (ret == ETOYWASMTRAP) { assert(ctx->trapped); const struct trap_info *trap = &ctx->trap; *trapp = trap; } +fail: return ret; } @@ -829,7 +858,8 @@ exec_func(struct exec_context *ctx, uint32_t funcidx, */ int toywasm_repl_invoke(struct repl_state *state, const char *modname, - const char *cmd, uint32_t *exitcodep, bool print_result) + const char *cmd, int timeout_ms, uint32_t *exitcodep, + bool print_result) { char *cmd1 = strdup(cmd); if (cmd1 == NULL) { @@ -910,7 +940,8 @@ toywasm_repl_invoke(struct repl_state *state, const char *modname, struct wasi_threads_instance *wasi_threads = state->wasi_threads; wasi_threads_setup_exec_context(wasi_threads, ctx); #endif - ret = exec_func(ctx, funcidx, ptype, rtype, param, result, &trap); + ret = exec_func(ctx, funcidx, ptype, rtype, param, result, timeout_ms, + &trap); #if defined(TOYWASM_ENABLE_WASI_THREADS) wasi_threads_complete_exec(wasi_threads, &trap); #endif @@ -1097,7 +1128,7 @@ repl_module_subcmd(struct repl_state *state, const char *cmd, goto fail; } } else if (!strcmp(cmd, "invoke") && opt != NULL) { - ret = toywasm_repl_invoke(state, modname, opt, NULL, true); + ret = toywasm_repl_invoke(state, modname, opt, -1, NULL, true); if (ret != 0) { goto fail; } diff --git a/cli/repl.h b/cli/repl.h index c99dcac4..5896d0ae 100644 --- a/cli/repl.h +++ b/cli/repl.h @@ -48,7 +48,7 @@ int toywasm_repl_load(struct repl_state *state, const char *modname, int toywasm_repl_register(struct repl_state *state, const char *modname, const char *register_name); int toywasm_repl_invoke(struct repl_state *state, const char *modname, - const char *cmd, uint32_t *exitcodep, + const char *cmd, int timeout_ms, uint32_t *exitcodep, bool print_result); void toywasm_repl_print_version(void);